Update pdfium to Chrome 114.0.5735.130 pdfium
pdfium last commit id: 9505810f6
Bug: 279055389
Test: Build the code and flash the device and check Print functionality
Test: atest FrameworksCoreTests
Test: atest CtsPrintTestCases
Test: atest CtsPdfTestCases
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:3e2fb7d98efb4ba7b51fd84e9a0ae04f8c0f7805)
Merged-In: I2efabeec0d0fa3925bcbeebf36031cee6f7f9fc4
Change-Id: I2efabeec0d0fa3925bcbeebf36031cee6f7f9fc4
diff --git a/testing/BUILD.gn b/testing/BUILD.gn
index 787aa6d..034de3c 100644
--- a/testing/BUILD.gn
+++ b/testing/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2018 The PDFium Authors. All rights reserved.
+# Copyright 2018 The PDFium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -7,16 +7,23 @@
source_set("test_support") {
testonly = true
sources = [
+ "command_line_helpers.cpp",
+ "command_line_helpers.h",
+ "font_renamer.cpp",
+ "font_renamer.h",
"fx_string_testhelpers.cpp",
"fx_string_testhelpers.h",
"invalid_seekable_read_stream.cpp",
"invalid_seekable_read_stream.h",
"pseudo_retainable.h",
+ "scoped_set_tz.cpp",
+ "scoped_set_tz.h",
"string_write_stream.cpp",
"string_write_stream.h",
+ "test_fonts.cpp",
+ "test_fonts.h",
"test_loader.cpp",
"test_loader.h",
- "test_support.cpp",
"test_support.h",
"utils/bitmap_saver.cpp",
"utils/bitmap_saver.h",
@@ -24,10 +31,12 @@
"utils/file_util.h",
"utils/hash.cpp",
"utils/hash.h",
- "utils/path_service.cpp",
- "utils/path_service.h",
]
data = [ "resources/" ]
+ public_deps = [
+ ":path_service",
+ "//third_party/test_fonts",
+ ]
deps = [
"../:pdfium_public_headers",
"../core/fdrm",
@@ -35,9 +44,11 @@
"../core/fxge",
"image_diff",
]
- configs += [ "../:pdfium_core_config" ]
+ configs += [
+ "../:pdfium_strict_config",
+ "../:pdfium_noshorten_config",
+ ]
visibility = [ "../*" ]
-
if (pdf_enable_v8) {
sources += [
"v8_initializer.cpp",
@@ -51,25 +62,92 @@
}
}
+source_set("path_service") {
+ testonly = true
+ sources = [
+ "utils/path_service.cpp",
+ "utils/path_service.h",
+ ]
+ deps = [ "../core/fxcrt" ]
+ configs += [
+ "../:pdfium_strict_config",
+ "../:pdfium_noshorten_config",
+ ]
+ visibility = [ "../*" ]
+}
+
+source_set("test_environments") {
+ testonly = true
+ sources = [
+ "pdf_test_environment.cpp",
+ "pdf_test_environment.h",
+ ]
+ deps = [
+ ":test_support",
+ "../core/fxcrt",
+ "../core/fxge",
+ "//testing/gtest",
+ ]
+ configs += [
+ "../:pdfium_strict_config",
+ "../:pdfium_noshorten_config",
+ ]
+ if (pdf_enable_v8) {
+ sources += [
+ "v8_test_environment.cpp",
+ "v8_test_environment.h",
+ ]
+ deps += [
+ "../fxjs",
+ "//v8",
+ "//v8:v8_libplatform",
+ ]
+ configs += [ "//v8:external_startup_data" ]
+ }
+ if (pdf_enable_xfa) {
+ sources += [
+ "xfa_test_environment.cpp",
+ "xfa_test_environment.h",
+ ]
+ deps += [
+ "../fxjs:gc",
+ "../xfa/fgas/font",
+ ]
+ }
+}
+
source_set("unit_test_support") {
testonly = true
sources = []
deps = []
-
- configs += [ "../:pdfium_core_config" ]
- visibility = [ "../*" ]
-
- if (pdf_enable_xfa) {
+ configs += [
+ "../:pdfium_strict_config",
+ "../:pdfium_noshorten_config",
+ ]
+ public_deps = [
+ ":test_environments",
+ ":test_support",
+ ]
+ if (pdf_enable_v8) {
sources += [
- "xfa_unit_test_support.cpp",
- "xfa_unit_test_support.h",
+ "fxv8_unittest.cpp",
+ "fxv8_unittest.h",
]
deps += [
- "../:pdfium",
- "../core/fxge",
- "../xfa/fgas",
+ "../fxjs",
"//testing/gtest",
]
+ configs += [ "//v8:external_startup_data" ]
+ if (pdf_enable_xfa) {
+ sources += [
+ "fxgc_unittest.cpp",
+ "fxgc_unittest.h",
+ ]
+ deps += [
+ "../fxjs:gc",
+ "//testing/gtest",
+ ]
+ }
}
}
@@ -78,6 +156,10 @@
sources = [
"embedder_test.cpp",
"embedder_test.h",
+ "embedder_test_constants.cpp",
+ "embedder_test_constants.h",
+ "embedder_test_environment.cpp",
+ "embedder_test_environment.h",
"embedder_test_mock_delegate.h",
"embedder_test_timer_handling_delegate.h",
"fake_file_access.cpp",
@@ -85,26 +167,37 @@
"range_set.cpp",
"range_set.h",
]
-
deps = [
- ":test_support",
"../:pdfium_public_headers",
"../core/fdrm",
"../core/fxcrt",
+ "../core/fxge",
"../third_party:pdfium_base",
"//testing/gmock",
"//testing/gtest",
]
- configs += [ "../:pdfium_core_config" ]
+ public_deps = [
+ ":test_environments",
+ ":test_support",
+ ]
+ configs += [
+ "../:pdfium_strict_config",
+ "../:pdfium_noshorten_config",
+ ]
visibility = [ "../*" ]
-
if (pdf_enable_v8) {
sources += [
+ "external_engine_embedder_test.cpp",
+ "external_engine_embedder_test.h",
"js_embedder_test.cpp",
"js_embedder_test.h",
]
- deps += [ "../fxjs" ]
-
+ deps += [
+ "../fxjs",
+ "//v8",
+ "//v8:v8_libplatform",
+ ]
+ configs += [ "//v8:external_startup_data" ]
if (pdf_enable_xfa) {
sources += [
"xfa_js_embedder_test.cpp",
@@ -119,3 +212,7 @@
}
}
}
+
+# Dummy group to keep satisfy references from //build.
+group("test_scripts_shared") {
+}
diff --git a/testing/SUPPRESSIONS b/testing/SUPPRESSIONS
index b3059e9..b50d3ce 100644
--- a/testing/SUPPRESSIONS
+++ b/testing/SUPPRESSIONS
@@ -1,4 +1,4 @@
-# Copyright 2016 The PDFium Authors. All rights reserved.
+# Copyright 2016 The PDFium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
#
@@ -10,6 +10,7 @@
# Column 1: platform: *, win, mac, linux
# Column 2: v8 support: *, nov8, v8
# Column 3: xfa support: *, noxfa, xfa
+# Column 4: rendering support: *, agg, skia
#
# All columns on a line on a line must match, but filenames may be repeated
# on subsequent lines to suppress more cases. Within each column, any one of
@@ -21,337 +22,379 @@
#
# Corpus tests
#
-050_extra_m.pdf mac,win * *
-12.pdf mac * *
-1_10_watermark.pdf * * *
-1_1_textbox.pdf * * *
-1_2_typewriter.pdf * * *
-1_3_callout.pdf * * *
-1_matrix.pdf mac * *
-1m_diff_lsjdf.pdf mac * *
-1m_same_xxxx.pdf mac * *
-2_11_stamp3.pdf mac * *
-2_6_textbox.pdf * * *
-2_color_calrgb.pdf mac * *
-2_color_indexed.pdf mac * *
-3_4_textbox.pdf * * *
-3_interpolate_image.pdf mac * *
-3bigpreview.pdf mac,win * *
-4_35.pdf mac * *
-4_39.pdf mac * *
-5.1.pdf mac * *
-5.2.pdf * * *
-5.5_simple_font.pdf mac,win * *
-8.2_name_dest_f_dest.pdf mac * *
-8.2_outline.pdf mac * *
-8.3_presentation.pdf mac * *
-FRC_10_8.2.2__T8.3_original_file.pdf * * *
-FRC_11_8.2.2__T8.3_first_last_exchange.pdf * * *
-FRC_12_8.2.2__T8.3_first_outline_obj_ID.pdf * * *
-FRC_13_8.2.2__T8.3_Count_edit300.pdf * * *
-FRC_14_8.2.2__T8.3_Count_edit0.pdf * * *
-FRC_15_8.2.2__T8.3_Count_edit1.pdf * * *
-FRC_16_8.2.2__T8.3_Count_edit_1.pdf * * *
-FRC_1_8.2.2__T8.3_First_empty.pdf * * *
-FRC_2_8.2.2__T8.3_Last_empty.pdf * * *
-FRC_3.5_P__3616_Password_1.pdf * * *
-FRC_3_8.2.2_Type_empty.pdf * * *
-FRC_4_8.2.2__T8.3_Count_empty.pdf * * *
-FRC_5_8.2.2__T8.3_First_remove.pdf * * *
-FRC_6_8.2.2__T8.3_Last_remove.pdf * * *
-FRC_7_8.2.2_Type_remove.pdf * * *
-FRC_8.4.1_Annotations_M.pdf * * *
-FRC_8.4.1_Annotations_NM.pdf * * *
-FRC_8.5_Page_C_SubmitForm.pdf * * *
-FRC_8.5_Page_PI_ResetForm_Phantom.pdf * * *
-FRC_8.5_Widget_F.pdf * nov8 *
-FRC_8_8.2.2__T8.3_Count_remove.pdf * * *
-FRC_9_8.2.2__T8.3_remove_first_item.pdf * * *
-action.pdf * * *
-action_execute_a_menu_item.pdf mac * *
-action_hide_show_form.pdf mac * *
-action_on_focus.pdf mac * *
-action_open_a_file.pdf mac * *
-action_pdf_save_close.pdf mac * *
-action_reset.pdf mac * *
-action_run_javascript.pdf mac * *
-action_submit_a_form.pdf mac * *
-all_trigger_alert.pdf * * *
-all_trigger_browsefordoc.pdf * * *
-all_trigger_mailmsg.pdf * * *
-all_trigger_newdoc.pdf * * *
-all_trigger_print.pdf * * *
-all_trigger_run_js_lunchurl.pdf mac * *
-all_trigger_run_js_maildoc.pdf mac * *
-annotation_highlight_author_content.pdf mac * *
-annotation_highlight_long_content.pdf mac * *
-annotation_highlight_no_author.pdf mac * *
-app_launchurl.pdf mac * *
-appstoredescription3.1_en_updated.pdf mac * *
-bookmark.pdf * * *
-bookmarkgetcolor.pdf mac * *
-bug_0_length_line.pdf mac * *
-bug_0_width_line.pdf mac * *
-bug_440132.pdf mac * *
-bug_white_space.pdf mac * *
-calcorderindex_test.pdf * * *
-calculate_average.pdf mac * *
-calculate_order.pdf * * *
-calculate_sum_a_b_c.pdf mac * *
-calculate_validate.pdf mac * *
-calculate_validate.pdf * nov8 *
-ch_1.pdf * * *
-check_box.pdf * * *
-color.pdf mac * *
-colorspace.pdf mac * *
-colorspace_test1.pdf mac * *
-combo_box.pdf * * *
-combo_box_format.pdf mac * *
-date.pdf mac * *
-edit_transform.pdf mac * *
-en_contact.pdf mac * *
-en_diy.pdf mac * *
-en_foxit.pdf mac * *
-en_fqa2.pdf mac * *
-en_introduce.pdf mac * *
-en_tem.pdf mac * *
-en_uicase_.pdf mac * *
-event.change.pdf mac * *
-event.changeex.pdf mac * *
-event.keydown.pdf mac * *
-event.keydown_1_.pdf mac * *
-event.type_name.pdf mac * *
-event.value.pdf mac * *
-event_change.pdf mac * *
-example_001.pdf mac * *
-example_002.pdf mac,win * *
-example_003.pdf mac,win * *
-example_004.pdf mac * *
-example_005.pdf mac * *
-example_006.pdf mac,win * *
-example_007.pdf mac * *
-example_008.pdf mac * *
-example_009.pdf mac * *
-example_010.pdf mac * *
-example_011.pdf mac * *
-example_012.pdf mac * *
-example_013.pdf mac * *
-example_014.pdf mac * *
-example_015.pdf mac * *
-example_016.pdf mac * *
-example_017.pdf mac * *
-example_018.pdf mac * *
-example_019.pdf mac * *
-example_020.pdf mac * *
-example_021.pdf mac * *
-example_022.pdf mac * *
-example_023.pdf mac * *
-example_024.pdf mac * *
-example_025.pdf mac * *
-example_026.pdf mac * *
-example_027.pdf mac * *
-example_028.pdf mac * *
-example_029.pdf mac * *
-example_030.pdf mac * *
-example_031.pdf mac * *
-example_032.pdf mac * *
-example_033.pdf mac,win * *
-example_034.pdf mac * *
-example_035.pdf mac,win * *
-example_036.pdf mac,win * *
-example_037.pdf mac * *
-example_038.pdf mac,win * *
-example_039.pdf mac * *
-example_040.pdf mac * *
-example_041.pdf mac,win * *
-example_042.pdf mac * *
-example_043.pdf mac * *
-example_044.pdf mac * *
-example_045.pdf mac,win * *
-example_046.pdf mac,win * *
-example_047.pdf mac * *
-example_048.pdf mac * *
-example_049.pdf mac * *
-example_050.pdf mac * *
-example_051.pdf mac * *
-example_052.pdf mac * *
-example_053.pdf mac * *
-example_054.pdf mac * *
-example_055.pdf mac,win * *
-example_056.pdf mac * *
-example_057.pdf mac * *
-example_058.pdf mac * *
-example_059.pdf mac,win * *
-example_060.pdf mac * *
-example_061.pdf mac,win * *
-example_062.pdf mac * *
-example_063.pdf mac * *
-example_064.pdf mac * *
-example_065.pdf mac * *
-fillform.pdf * * *
-form_action_trigger.pdf * * *
-form_button_sign_url.pdf * * *
-form_combo_sign_url.pdf * * *
-form_combobox0.pdf * * *
-form_combobox_actioin_goto.pdf * * *
-form_combobox_date.pdf mac * *
-form_combobox_date.pdf * nov8 *
-form_combobox_date1.pdf * * *
-form_combobox_date2.pdf mac * *
-form_combobox_date2.pdf * nov8 *
-form_combobox_importform.pdf * * *
-form_combobox_num.pdf mac * *
-form_combobox_num.pdf * nov8 *
-form_combobox_per.pdf mac * *
-form_combobox_per.pdf * nov8 *
-form_combobox_plus.pdf mac * *
-form_combobox_plus.pdf * nov8 *
-form_combobox_product.pdf mac * *
-form_combobox_product.pdf * nov8 *
-form_combobox_resetform.pdf * * *
-form_combobox_time.pdf mac * *
-form_combobox_time.pdf * nov8 *
-form_list.pdf * * *
-form_list1.pdf * * *
-form_same.pdf mac * *
-form_text_sign_url.pdf * * *
-format_combo_box.pdf mac * *
-format_combo_box.pdf * nov8 *
-format_custom_format.pdf linux,mac * *
-format_custom_format.pdf * nov8 *
-format_custom_keystroke.pdf * * *
-format_date.pdf * nov8 *
-format_number.pdf mac * *
-format_percentage.pdf mac * *
-format_special.pdf * nov8 *
-format_text_color.pdf mac * *
-formfeild.pdf * * *
-getarray.pdf mac * *
-javascriptaction.pdf * * *
-jetman_std.pdf mac * *
-jetman_std_fixed.pdf mac * *
-js_calculate.pdf * * *
-list_box.pdf * * *
-negative.pdf mac * *
-new_certify1.pdf mac * *
-new_signature1.pdf mac * *
-new_signature2.pdf mac * *
-new_stamp4.pdf mac * *
-new_stamp5.pdf mac * *
-new_textmarkup1.pdf mac * *
-new_textmarkup1_hidden.pdf mac * *
-new_textmarkup2.pdf mac * *
-new_textmarkup2_hidden.pdf mac * *
-new_textmarkup4.pdf mac * *
-new_textmarkup4_hidden.pdf mac * *
-new_textmarkup5.pdf mac * *
-new_textmarkup5_hidden.pdf mac * *
-new_textmarkup6.pdf mac * *
-new_textmarkup7.pdf mac * *
-new_textmarkup7_hidden.pdf mac * *
-new_textmarkup8.pdf mac * *
-new_textmarkup8_hidden.pdf mac * *
-number.pdf * * *
-octest.pdf mac * *
-open_a_weblink.pdf mac * *
-path_10_jd.pdf mac * *
-path_5_pattern.pdf mac * *
-path_6_graphics4.5.5.pdf mac * *
-path_7.pdf mac * *
-path_9.pdf mac * *
-percentage.pdf mac * *
-push_button.pdf * * *
-quick_start_guide.pdf mac * *
-radio_button.pdf * * *
-run_custom_validate_script.pdf * * *
-show_1.pdf mac * *
-signature.pdf * * *
-signature_4.pdf * * *
-simplified_field_notation.pdf mac * *
-special.pdf mac * *
-submit_form.pdf mac * *
-test_app_beep.pdf * * *
-test_control.pdf * * *
-test_m.pdf mac,win * *
-text_field.pdf * * *
-text_field_font_input_decimal_point.pdf mac * *
-text_field_multiline_line_spacing.pdf mac * *
-thread_action.pdf mac * *
-time.pdf mac * *
-transformation.pdf mac * *
-transparent.pdf mac * *
-whats_new_in_v3.0.pdf mac * *
-widget_javascript.pdf mac * *
-zh_file1.pdf mac * *
-zh_function_list.pdf mac * *
-zh_shared_document.pdf mac * *
+12.pdf mac * * agg
+1_1_textbox.pdf * * * *
+1_2_typewriter.pdf * * * *
+1_3_callout.pdf * * * *
+1_matrix.pdf mac * * agg
+1m_diff_lsjdf.pdf mac * * agg
+1m_same_xxxx.pdf mac * * agg
+2_11_stamp3.pdf mac * * agg
+2_6_textbox.pdf * * * *
+2_color_calrgb.pdf mac * * agg
+2_color_indexed.pdf mac * * agg
+3_4_textbox.pdf * * * *
+3_interpolate_image.pdf mac * * agg
+3bigpreview.pdf mac * * agg
+4_35.pdf mac * * agg
+4_39.pdf mac * * agg
+5.1.pdf mac * * agg
+5.2.pdf * * * *
+5.5_simple_font.pdf mac * * agg
+8.2_name_dest_f_dest.pdf mac * * agg
+8.2_outline.pdf mac * * agg
+8.3_presentation.pdf mac * * agg
+FRC_10_8.2.2__T8.3_original_file.pdf * * * *
+FRC_11_8.2.2__T8.3_first_last_exchange.pdf * * * *
+FRC_12_8.2.2__T8.3_first_outline_obj_ID.pdf * * * *
+FRC_13_8.2.2__T8.3_Count_edit300.pdf * * * *
+FRC_14_8.2.2__T8.3_Count_edit0.pdf * * * *
+FRC_15_8.2.2__T8.3_Count_edit1.pdf * * * *
+FRC_16_8.2.2__T8.3_Count_edit_1.pdf * * * *
+FRC_1_8.2.2__T8.3_First_empty.pdf * * * *
+FRC_2_8.2.2__T8.3_Last_empty.pdf * * * *
+FRC_3.5_P__3616_Password_1.pdf * * * *
+FRC_3_8.2.2_Type_empty.pdf * * * *
+FRC_4_8.2.2__T8.3_Count_empty.pdf * * * *
+FRC_5_8.2.2__T8.3_First_remove.pdf * * * *
+FRC_6_8.2.2__T8.3_Last_remove.pdf * * * *
+FRC_7_8.2.2_Type_remove.pdf * * * *
+FRC_8.4.1_Annotations_M.pdf * * * *
+FRC_8.4.1_Annotations_NM.pdf * * * *
+FRC_8.5_Page_C_SubmitForm.pdf * * * *
+FRC_8.5_Page_PI_ResetForm_Phantom.pdf * * * *
+FRC_8.5_Widget_F.pdf * nov8 * *
+FRC_8_8.2.2__T8.3_Count_remove.pdf * * * *
+FRC_9_8.2.2__T8.3_remove_first_item.pdf * * * *
+action.pdf * * * *
+action_execute_a_menu_item.pdf mac * * agg
+action_hide_show_form.pdf mac * * agg
+action_on_focus.pdf mac * * agg
+action_open_a_file.pdf mac * * agg
+action_pdf_save_close.pdf mac * * agg
+action_reset.pdf mac * * agg
+action_run_javascript.pdf mac * * agg
+action_submit_a_form.pdf mac * * agg
+all_trigger_alert.pdf * * * *
+all_trigger_mailmsg.pdf * * * *
+all_trigger_print.pdf * * * *
+all_trigger_run_js_lunchurl.pdf mac * * agg
+all_trigger_run_js_maildoc.pdf mac * * agg
+annotation_highlight_author_content.pdf mac * * agg
+annotation_highlight_long_content.pdf mac * * agg
+annotation_highlight_no_author.pdf mac * * agg
+app_launchurl.pdf mac * * agg
+appstoredescription3.1_en_updated.pdf mac * * agg
+bookmark.pdf * * * *
+bookmarkgetcolor.pdf mac * * agg
+bug_0_length_line.pdf mac * * agg
+
+# TODO(pdfium:1812): Remove after associated bug is fixed
+bug_0_length_line.pdf * * * skia
+
+bug_0_width_line.pdf mac * * agg
+bug_440132.pdf mac * * agg
+bug_white_space.pdf mac * * agg
+calcorderindex_test.pdf * * * *
+calculate_average.pdf mac * * agg
+calculate_order.pdf * * * *
+calculate_sum_a_b_c.pdf mac * * agg
+calculate_validate.pdf mac * * agg
+calculate_validate.pdf * nov8 * *
+ch_1.pdf * * * *
+check_box.pdf * * * *
+color.pdf mac * * agg
+colorspace.pdf mac * * agg
+colorspace_test1.pdf mac * * agg
+combo_box.pdf * * * *
+combo_box_format.pdf mac * * agg
+date.pdf mac * * agg
+edit_transform.pdf mac * * agg
+en_contact.pdf mac * * agg
+en_diy.pdf mac * * agg
+en_foxit.pdf mac * * agg
+en_fqa2.pdf mac * * agg
+en_introduce.pdf mac * * agg
+en_tem.pdf mac * * agg
+en_uicase_.pdf mac * * agg
+event.change.pdf mac * * agg
+event.changeex.pdf mac * * agg
+event.keydown.pdf mac * * agg
+event.keydown_1_.pdf mac * * agg
+event.type_name.pdf mac * * agg
+event.value.pdf mac * * agg
+event_change.pdf mac * * agg
+example_001.pdf mac * * agg
+example_002.pdf mac * * agg
+example_003.pdf mac * * agg
+example_004.pdf mac * * agg
+example_005.pdf mac * * agg
+example_006.pdf mac * * agg
+example_007.pdf mac * * agg
+example_008.pdf mac * * agg
+example_009.pdf mac * * agg
+example_010.pdf mac * * agg
+example_011.pdf mac * * agg
+example_012.pdf mac * * agg
+example_013.pdf mac * * agg
+example_014.pdf mac * * agg
+example_015.pdf mac * * agg
+example_016.pdf mac * * agg
+example_017.pdf mac * * agg
+example_018.pdf mac * * agg
+example_019.pdf mac * * agg
+example_020.pdf mac * * agg
+example_021.pdf mac * * agg
+example_022.pdf mac * * agg
+example_023.pdf mac * * agg
+example_024.pdf mac * * agg
+example_025.pdf mac * * agg
+example_026.pdf mac * * agg
+example_027.pdf mac * * agg
+example_028.pdf mac * * agg
+example_029.pdf mac * * agg
+example_030.pdf mac * * agg
+example_031.pdf mac * * agg
+example_032.pdf mac * * agg
+example_033.pdf mac * * agg
+example_034.pdf mac * * agg
+example_035.pdf mac * * agg
+example_036.pdf mac * * agg
+example_037.pdf mac * * agg
+example_038.pdf mac * * agg
+example_039.pdf mac * * agg
+example_040.pdf mac * * agg
+example_041.pdf mac * * agg
+example_042.pdf mac * * agg
+example_043.pdf mac * * agg
+example_044.pdf mac * * agg
+example_045.pdf mac * * agg
+example_046.pdf mac * * agg
+example_047.pdf mac * * agg
+example_048.pdf mac * * agg
+example_049.pdf mac * * agg
+example_050.pdf mac * * agg
+example_051.pdf mac * * agg
+example_052.pdf mac * * agg
+example_053.pdf mac * * agg
+example_054.pdf mac * * agg
+example_055.pdf mac * * agg
+example_056.pdf mac * * agg
+example_057.pdf mac * * agg
+example_058.pdf mac * * agg
+example_059.pdf mac * * agg
+example_060.pdf mac * * agg
+example_061.pdf mac * * agg
+example_062.pdf mac * * agg
+example_063.pdf mac * * agg
+example_064.pdf mac * * agg
+example_065.pdf mac * * agg
+fillform.pdf * * * *
+form_action_trigger.pdf * * * *
+form_button_sign_url.pdf * * * *
+form_combo_sign_url.pdf * * * *
+form_combobox0.pdf * * * *
+form_combobox_actioin_goto.pdf * * * *
+form_combobox_date.pdf mac * * agg
+form_combobox_date.pdf * nov8 * *
+form_combobox_date1.pdf * * * *
+form_combobox_date2.pdf mac * * agg
+form_combobox_date2.pdf * nov8 * *
+form_combobox_importform.pdf * * * *
+form_combobox_num.pdf mac * * agg
+form_combobox_num.pdf * nov8 * *
+form_combobox_per.pdf mac * * agg
+form_combobox_per.pdf * nov8 * *
+form_combobox_plus.pdf mac * * agg
+form_combobox_plus.pdf * nov8 * *
+form_combobox_product.pdf mac * * agg
+form_combobox_product.pdf * nov8 * *
+form_combobox_resetform.pdf * * * *
+form_combobox_time.pdf mac * * agg
+form_combobox_time.pdf * nov8 * *
+form_list.pdf * * * *
+form_list1.pdf * * * *
+form_same.pdf mac * * agg
+form_text_sign_url.pdf * * * *
+format_combo_box.pdf mac * * agg
+format_combo_box.pdf * nov8 * *
+format_custom_format.pdf * nov8 * *
+format_custom_keystroke.pdf * * * *
+format_date.pdf * nov8 * *
+format_number.pdf mac * * agg
+format_percentage.pdf mac * * agg
+format_special.pdf * nov8 * *
+format_text_color.pdf mac * * agg
+formfield.pdf * * * *
+getarray.pdf mac * * agg
+
+# TODO(pdfium:489): Remove after associated bug is fixed.
+gradient_many_stops.pdf * * * agg
+
+javascriptaction.pdf * * * *
+jetman_std.pdf mac * * agg
+jetman_std_fixed.pdf mac * * agg
+js_calculate.pdf * * * *
+list_box.pdf * * * *
+negative.pdf mac * * agg
+new_certify1.pdf mac * * agg
+new_signature1.pdf mac * * agg
+new_signature2.pdf mac * * agg
+new_stamp4.pdf mac * * agg
+new_stamp5.pdf mac * * agg
+new_textmarkup1.pdf mac * * agg
+new_textmarkup1_hidden.pdf mac * * agg
+new_textmarkup2.pdf mac * * agg
+new_textmarkup2_hidden.pdf mac * * agg
+new_textmarkup4.pdf mac * * agg
+new_textmarkup4_hidden.pdf mac * * agg
+new_textmarkup5.pdf mac * * agg
+new_textmarkup5_hidden.pdf mac * * agg
+new_textmarkup6.pdf mac * * agg
+new_textmarkup7.pdf mac * * agg
+new_textmarkup7_hidden.pdf mac * * agg
+new_textmarkup8.pdf mac * * agg
+new_textmarkup8_hidden.pdf mac * * agg
+number.pdf * * * *
+octest.pdf mac * * agg
+open_a_weblink.pdf mac * * agg
+path_10_jd.pdf mac * * agg
+path_5_pattern.pdf mac * * agg
+path_6_graphics4.5.5.pdf mac * * agg
+path_7.pdf mac * * agg
+path_9.pdf mac * * agg
+percentage.pdf mac * * agg
+push_button.pdf * * * *
+quick_start_guide.pdf mac * * agg
+radio_button.pdf * * * *
+run_custom_validate_script.pdf * * * *
+show_1.pdf mac * * agg
+signature.pdf * * * *
+signature_4.pdf * * * *
+simplified_field_notation.pdf mac * * agg
+special.pdf mac * * agg
+submit_form.pdf mac * * agg
+test_app_beep.pdf * * * *
+test_control.pdf * * * *
+text_field.pdf * * * *
+text_field_font_input_decimal_point.pdf mac * * agg
+text_field_multiline_line_spacing.pdf mac * * agg
+thread_action.pdf mac * * agg
+time.pdf mac * * agg
+transformation.pdf mac * * agg
+transparent.pdf mac * * agg
+whats_new_in_v3.0.pdf mac * * agg
+widget_javascript.pdf mac * * agg
+
+# TODO(pdfium:1990): Remove after associated bug is fixed
+xfermodes2.pdf * * * agg
+
+zh_file1.pdf mac * * agg
+zh_function_list.pdf mac * * agg
+zh_shared_document.pdf mac * * agg
# TODO(hnakashima): These might never have been run. Go over them and fix.
-FRC_8.5_E&X.pdf * * *
-FRC_8.5_O&PO_GoToE.pdf * * *
-FRC_8.5_OpenAction&O_URI.pdf * * *
-FRC_8.5_PC&C_GoToE_T_T.pdf * * *
-FRC_8.5_PC_GoToE_T_R&P&A.pdf * * *
-FRC_8.5_PO_GoToE_T_R&N.pdf * * *
+FRC_8.5_E&X.pdf * * * *
+FRC_8.5_O&PO_GoToE.pdf * * * *
+FRC_8.5_OpenAction&O_URI.pdf * * * *
+FRC_8.5_PC&C_GoToE_T_T.pdf * * * *
+FRC_8.5_PC_GoToE_T_R&P&A.pdf * * * *
+FRC_8.5_PO_GoToE_T_R&N.pdf * * * *
# xfa_specific
-Choose.pdf * * *
-data_binding.pdf * * *
+Choose.pdf * * * *
+data_binding.pdf * * * *
# TODO(npm): Add proper evt for MouseEvents.
-MouseEvents_enter.pdf * * *
-MouseEvents_exit.pdf * * *
-Oneof3.pdf * * *
-Sum.pdf * * *
-TimeField.pdf win,linux * *
-Test_CheckBox.pdf * * *
-Test_DateField_locale_zh_HK.pdf mac,win * *
+MouseEvents_enter.pdf * * * *
+MouseEvents_exit.pdf * * * *
+Oneof3.pdf * * * *
+Sum.pdf * * * *
+Test_CheckBox.pdf * * * *
#
# JavaScript tests
#
-bug_679642.in * * noxfa
-bug_679643.in * * noxfa
-bug_735912.in * * noxfa
+bug_679642.in * * noxfa *
+bug_679643.in * * noxfa *
+bug_735912.in * * noxfa *
+
+# JS tests in nov8 mode expect empty results. This one will
+# not be empty as the callback is not js-based.
+named_action.in * nov8 * *
# xfa_specific
# TODO(pdfium:1106): Remove after associated bug is fixed
-resolve_nodes_1.pdf * * *
+resolve_nodes_1.pdf * * * *
#
# Pixel tests
#
-bug_492.in * nov8 *
-# TODO(pdfium:304): Remove after associated bug is fixed
-bug_304.pdf * * *
+# TODO(pdfium:1747): Remove after associated bug is fixed
+bug_1258634.in * * * *
# TODO(pdfium:1331): Remove after associated bug is fixed
-bug_1331.in * * *
+bug_1331.in * * * *
-# TODO(pdfium:1461): Remove after associated bug is fixed
-bug_1402.in win * *
+# TODO(chromium:1356149): Remove after associated bug is fixed
+bug_1356149.in mac * * agg
# TODO(pdfium:1457): Remove after associated bug is fixed
-bug_1457.in * * *
+bug_1457.in * * * *
+
+# TODO(pdfium:1519): Remove after associated bug is fixed
+bug_1519.in * * * *
+
+# TODO(pdfium:1571): Remove after associated bug is fixed
+bug_1571.in * * * *
+
+# TODO(pdfium:1723): Remove after associated bug is fixed
+bug_1723.in * * * *
+
+# TODO(pdfium:1972): Remove after associated bug is fixed
+bug_1972_1.in * * * agg
+bug_1972_2.in * * * agg
+bug_1972_3.in * * * agg
+
+# TODO(pdfium:1973): Remove after associated bug is fixed
+bug_1973.in * * * *
+
+# TODO(pdfium:2001): Remove after associated bug is fixed
+bug_2001.pdf * * * *
+
+# TODO(chromium:237527): Remove after associated bug is fixed
+bug_237527_1.in * * * *
# TODO(chromium:451366): Remove after associated bug is fixed
-bug_451366.in * * *
+bug_451366.in * * * agg
-# TODO(chromium:1012369): Remove after associated bug is fixed
-bug_1012369.in * * *
+# TODO(pdfium:492): Remove after associated bug is fixed
+bug_492.in * nov8 * *
+
+# TODO(chromium:725555, skia:9265): Remove after associated bug is fixed
+bug_725555.in * * * skia
+
+# TODO(chromium:983289): Remove after associated bug is fixed
+bug_983289.in * * * agg
+
+# TODO(pdfium:1747): Remove after associated bug is fixed
+jpxdecode.in * * * *
+jpxdecode_without_bitspercomponent.in * * * *
+jpxdecode_without_colorspace.in * * * *
+
+# TODO(chromium:1028991): Remove after associated bug is fixed
+reset_button.in * * * *
# xfa_specific
+# TODO(pdfium:1095): Remove after associated bug is fixed
+bug_997412.in win * * *
# TODO(pdfium:1107): Remove after associated bug is fixed
-standard_symbols.pdf * * *
-# TODO(pdfium:1168): Remove after associated bug is fixed
-xfa_bmp_image.in * * *
+standard_symbols.pdf * * * *
# TODO(pdfium:1095): Remove after associated bug is fixed
-xfa_example.in win * *
-# TODO(pdfium:1167): Remove after associated bug is fixed
-xfa_gif_image.in * * *
+xfa_example.in win * * *
# TODO(pdfium:1095): Remove after associated bug is fixed
-xfa_textfield.in win * *
+xfa_textfield.in win * * *
diff --git a/testing/SUPPRESSIONS_EXACT_MATCHING b/testing/SUPPRESSIONS_EXACT_MATCHING
new file mode 100644
index 0000000..7c260d1
--- /dev/null
+++ b/testing/SUPPRESSIONS_EXACT_MATCHING
@@ -0,0 +1,28 @@
+# Copyright 2023 The PDFium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+#
+# List of tests to use fuzzy instead of exact matching, one per line.
+# There are four space-separated columns per line
+# Each column (except column 0) can contain a comma-separated list of values.
+#
+# Column 0: test file name
+# Column 1: platform: *, win, mac, linux
+# Column 2: v8 support: *, nov8, v8
+# Column 3: xfa support: *, noxfa, xfa
+# Column 4: rendering support: *, agg, skia
+#
+# All columns on a line on a line must match, but filenames may be repeated
+# on subsequent lines to suppress more cases. Within each column, any one of
+# the comma-separated values must match in order for the colum to "match".
+# The filenames and keywords are case-sensitive.
+#
+# Try to keep the file alphabetized within each category of test.
+
+#
+# Corpus tests
+#
+
+# Device-specific ColorBurn differences (see pdfium:1959).
+xfermodes2.pdf * * * skia
+xfermodes3.pdf * * * skia
diff --git a/testing/SUPPRESSIONS_IMAGE_DIFF b/testing/SUPPRESSIONS_IMAGE_DIFF
index cdb2d66..e501756 100644
--- a/testing/SUPPRESSIONS_IMAGE_DIFF
+++ b/testing/SUPPRESSIONS_IMAGE_DIFF
@@ -1,4 +1,4 @@
-# Copyright 2017 The PDFium Authors. All rights reserved.
+# Copyright 2017 The PDFium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
#
@@ -10,6 +10,7 @@
# Column 1: platform: *, win, mac, linux
# Column 2: v8 support: *, nov8, v8
# Column 3: xfa support: *, noxfa, xfa
+# Column 4: rendering support: *, agg, skia
#
# All columns on a line on a line must match, but filenames may be repeated
# on subsequent lines to suppress more cases. Within each column, any one of
@@ -21,12 +22,12 @@
#
# Corpus tests
#
-FRC_3.5_CF_Strf_stmf_DefaultCryptFilter.pdf * * *
-FRC_3.5_EncryptMetadata_T.pdf * * *
-FRC_3.5_Encrypt_is_damage.pdf * * *
-FRC_3.5_Filter_PubSec_SubFilter_s5.pdf * * *
-FRC_3.5_Filter_PubSec_Sub_SubFilter_s4.pdf * * *
-MouseEvents.pdf * * *
-Oneof.pdf * * *
-bug_651304.pdf * * *
-outline.pdf * * *
+FRC_3.5_CF_Strf_stmf_DefaultCryptFilter.pdf * * * *
+FRC_3.5_EncryptMetadata_T.pdf * * * *
+FRC_3.5_Encrypt_is_damage.pdf * * * *
+FRC_3.5_Filter_PubSec_SubFilter_s5.pdf * * * *
+FRC_3.5_Filter_PubSec_Sub_SubFilter_s4.pdf * * * *
+MouseEvents.pdf * * * *
+Oneof.pdf * * * *
+bug_651304.pdf * * * *
+outline.pdf * * * *
diff --git a/testing/command_line_helpers.cpp b/testing/command_line_helpers.cpp
new file mode 100644
index 0000000..758f278
--- /dev/null
+++ b/testing/command_line_helpers.cpp
@@ -0,0 +1,23 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/command_line_helpers.h"
+
+bool ParseSwitchKeyValue(const std::string& arg,
+ const std::string& key,
+ std::string* value) {
+ if (arg.size() <= key.size() || arg.compare(0, key.size(), key) != 0)
+ return false;
+
+ *value = arg.substr(key.size());
+ return true;
+}
+
+FPDF_RENDERER_TYPE GetDefaultRendererType() {
+#if defined(_SKIA_SUPPORT_)
+ return FPDF_RENDERERTYPE_SKIA;
+#else
+ return FPDF_RENDERERTYPE_AGG;
+#endif
+}
diff --git a/testing/command_line_helpers.h b/testing/command_line_helpers.h
new file mode 100644
index 0000000..cc134cb
--- /dev/null
+++ b/testing/command_line_helpers.h
@@ -0,0 +1,23 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TESTING_COMMAND_LINE_HELPERS_H_
+#define TESTING_COMMAND_LINE_HELPERS_H_
+
+#include <string>
+
+#include "public/fpdfview.h"
+
+// Extract the value from a keyed command line argument.
+// `arg` is expected to be "--key=value", and `key` is "--key=".
+bool ParseSwitchKeyValue(const std::string& arg,
+ const std::string& key,
+ std::string* value);
+
+// Identifies the compile-time default 2D graphics library to use for rendering
+// to FPDF_BITMAPs. Used as part of support to override the renderer at runtime
+// based upon command line options.
+FPDF_RENDERER_TYPE GetDefaultRendererType();
+
+#endif // TESTING_COMMAND_LINE_HELPERS_H_
diff --git a/testing/embedder_test.cpp b/testing/embedder_test.cpp
index 9bf4a18..366088d 100644
--- a/testing/embedder_test.cpp
+++ b/testing/embedder_test.cpp
@@ -1,13 +1,10 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "testing/embedder_test.h"
-#include <limits.h>
-
-#include <list>
-#include <map>
+#include <algorithm>
#include <memory>
#include <string>
#include <utility>
@@ -19,20 +16,17 @@
#include "public/fpdf_edit.h"
#include "public/fpdf_text.h"
#include "public/fpdfview.h"
+#include "testing/embedder_test_environment.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/test_loader.h"
#include "testing/utils/bitmap_saver.h"
#include "testing/utils/file_util.h"
#include "testing/utils/hash.h"
#include "testing/utils/path_service.h"
-#include "third_party/base/logging.h"
-#include "third_party/base/ptr_util.h"
-#include "third_party/base/stl_util.h"
-
-#ifdef PDF_ENABLE_V8
-#include "v8/include/v8-platform.h"
-#include "v8/include/v8.h"
-#endif // PDF_ENABLE_V8
+#include "third_party/base/check.h"
+#include "third_party/base/containers/contains.h"
+#include "third_party/base/notreached.h"
+#include "third_party/base/numerics/safe_conversions.h"
namespace {
@@ -40,7 +34,7 @@
return EmbedderTest::BytesPerPixelForFormat(FPDFBitmap_GetFormat(bitmap));
}
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
int CALLBACK GetRecordProc(HDC hdc,
HANDLETABLE* handle_table,
const ENHMETARECORD* record,
@@ -50,12 +44,224 @@
records.push_back(record);
return 1;
}
-#endif // defined(OS_WIN)
+#endif // BUILDFLAG(IS_WIN)
+
+// These "jump" into the delegate to do actual testing.
+void UnsupportedHandlerTrampoline(UNSUPPORT_INFO* info, int type) {
+ auto* delegate = static_cast<EmbedderTest*>(info)->GetDelegate();
+ delegate->UnsupportedHandler(type);
+}
+
+int AlertTrampoline(IPDF_JSPLATFORM* platform,
+ FPDF_WIDESTRING message,
+ FPDF_WIDESTRING title,
+ int type,
+ int icon) {
+ auto* delegate = static_cast<EmbedderTest*>(platform)->GetDelegate();
+ return delegate->Alert(message, title, type, icon);
+}
+
+int SetTimerTrampoline(FPDF_FORMFILLINFO* info, int msecs, TimerCallback fn) {
+ auto* delegate = static_cast<EmbedderTest*>(info)->GetDelegate();
+ return delegate->SetTimer(msecs, fn);
+}
+
+void KillTimerTrampoline(FPDF_FORMFILLINFO* info, int id) {
+ auto* delegate = static_cast<EmbedderTest*>(info)->GetDelegate();
+ return delegate->KillTimer(id);
+}
+
+FPDF_PAGE GetPageTrampoline(FPDF_FORMFILLINFO* info,
+ FPDF_DOCUMENT document,
+ int page_index) {
+ auto* delegate = static_cast<EmbedderTest*>(info)->GetDelegate();
+ return delegate->GetPage(info, document, page_index);
+}
+
+void DoURIActionTrampoline(FPDF_FORMFILLINFO* info, FPDF_BYTESTRING uri) {
+ auto* delegate = static_cast<EmbedderTest*>(info)->GetDelegate();
+ return delegate->DoURIAction(uri);
+}
+
+void DoGoToActionTrampoline(FPDF_FORMFILLINFO* info,
+ int page_index,
+ int zoom_mode,
+ float* pos_array,
+ int array_size) {
+ auto* delegate = static_cast<EmbedderTest*>(info)->GetDelegate();
+ return delegate->DoGoToAction(info, page_index, zoom_mode, pos_array,
+ array_size);
+}
+
+void OnFocusChangeTrampoline(FPDF_FORMFILLINFO* info,
+ FPDF_ANNOTATION annot,
+ int page_index) {
+ auto* delegate = static_cast<EmbedderTest*>(info)->GetDelegate();
+ return delegate->OnFocusChange(info, annot, page_index);
+}
+
+void DoURIActionWithKeyboardModifierTrampoline(FPDF_FORMFILLINFO* info,
+ FPDF_BYTESTRING uri,
+ int modifiers) {
+ auto* delegate = static_cast<EmbedderTest*>(info)->GetDelegate();
+ return delegate->DoURIActionWithKeyboardModifier(info, uri, modifiers);
+}
+
+// These do nothing (but must return a reasonable default value).
+void InvalidateStub(FPDF_FORMFILLINFO* pThis,
+ FPDF_PAGE page,
+ double left,
+ double top,
+ double right,
+ double bottom) {}
+
+void OutputSelectedRectStub(FPDF_FORMFILLINFO* pThis,
+ FPDF_PAGE page,
+ double left,
+ double top,
+ double right,
+ double bottom) {}
+
+void SetCursorStub(FPDF_FORMFILLINFO* pThis, int nCursorType) {}
+
+FPDF_SYSTEMTIME GetLocalTimeStub(FPDF_FORMFILLINFO* pThis) {
+ return {122, 11, 6, 28, 12, 59, 59, 500};
+}
+
+void OnChangeStub(FPDF_FORMFILLINFO* pThis) {}
+
+FPDF_PAGE GetCurrentPageStub(FPDF_FORMFILLINFO* pThis, FPDF_DOCUMENT document) {
+ return GetPageTrampoline(pThis, document, 0);
+}
+
+int GetRotationStub(FPDF_FORMFILLINFO* pThis, FPDF_PAGE page) {
+ return 0;
+}
+
+void ExecuteNamedActionStub(FPDF_FORMFILLINFO* pThis, FPDF_BYTESTRING name) {}
+
+void SetTextFieldFocusStub(FPDF_FORMFILLINFO* pThis,
+ FPDF_WIDESTRING value,
+ FPDF_DWORD valueLen,
+ FPDF_BOOL is_focus) {}
+
+#ifdef PDF_ENABLE_XFA
+void DisplayCaretStub(FPDF_FORMFILLINFO* pThis,
+ FPDF_PAGE page,
+ FPDF_BOOL bVisible,
+ double left,
+ double top,
+ double right,
+ double bottom) {}
+
+int GetCurrentPageIndexStub(FPDF_FORMFILLINFO* pThis, FPDF_DOCUMENT document) {
+ return 0;
+}
+
+void SetCurrentPageStub(FPDF_FORMFILLINFO* pThis,
+ FPDF_DOCUMENT document,
+ int iCurPage) {}
+
+void GotoURLStub(FPDF_FORMFILLINFO* pThis,
+ FPDF_DOCUMENT document,
+ FPDF_WIDESTRING wsURL) {}
+
+void GetPageViewRectStub(FPDF_FORMFILLINFO* pThis,
+ FPDF_PAGE page,
+ double* left,
+ double* top,
+ double* right,
+ double* bottom) {
+ *left = 0.0;
+ *top = 0.0;
+ *right = 512.0;
+ *bottom = 512.0;
+}
+
+void PageEventStub(FPDF_FORMFILLINFO* pThis,
+ int page_count,
+ FPDF_DWORD event_type) {}
+
+FPDF_BOOL PopupMenuStub(FPDF_FORMFILLINFO* pThis,
+ FPDF_PAGE page,
+ FPDF_WIDGET hWidget,
+ int menuFlag,
+ float x,
+ float y) {
+ return true;
+}
+
+FPDF_FILEHANDLER* OpenFileStub(FPDF_FORMFILLINFO* pThis,
+ int fileFlag,
+ FPDF_WIDESTRING wsURL,
+ const char* mode) {
+ return nullptr;
+}
+
+void EmailToStub(FPDF_FORMFILLINFO* pThis,
+ FPDF_FILEHANDLER* fileHandler,
+ FPDF_WIDESTRING pTo,
+ FPDF_WIDESTRING pSubject,
+ FPDF_WIDESTRING pCC,
+ FPDF_WIDESTRING pBcc,
+ FPDF_WIDESTRING pMsg) {}
+
+void UploadToStub(FPDF_FORMFILLINFO* pThis,
+ FPDF_FILEHANDLER* fileHandler,
+ int fileFlag,
+ FPDF_WIDESTRING uploadTo) {}
+
+int GetPlatformStub(FPDF_FORMFILLINFO* pThis, void* platform, int length) {
+ return 0;
+}
+
+int GetLanguageStub(FPDF_FORMFILLINFO* pThis, void* language, int length) {
+ return 0;
+}
+
+FPDF_FILEHANDLER* DownloadFromURLStub(FPDF_FORMFILLINFO* pThis,
+ FPDF_WIDESTRING URL) {
+ static const char kString[] = "<body>secrets</body>";
+ static FPDF_FILEHANDLER kFakeFileHandler = {
+ nullptr,
+ [](void*) -> void {},
+ [](void*) -> FPDF_DWORD { return sizeof(kString); },
+ [](void*, FPDF_DWORD off, void* buffer, FPDF_DWORD size) -> FPDF_RESULT {
+ memcpy(buffer, kString, std::min<size_t>(size, sizeof(kString)));
+ return 0;
+ },
+ [](void*, FPDF_DWORD, const void*, FPDF_DWORD) -> FPDF_RESULT {
+ return -1;
+ },
+ [](void*) -> FPDF_RESULT { return 0; },
+ [](void*, FPDF_DWORD) -> FPDF_RESULT { return 0; }};
+ return &kFakeFileHandler;
+}
+
+FPDF_BOOL PostRequestURLStub(FPDF_FORMFILLINFO* pThis,
+ FPDF_WIDESTRING wsURL,
+ FPDF_WIDESTRING wsData,
+ FPDF_WIDESTRING wsContentType,
+ FPDF_WIDESTRING wsEncode,
+ FPDF_WIDESTRING wsHeader,
+ FPDF_BSTR* response) {
+ const char kString[] = "p\0o\0s\0t\0e\0d\0";
+ FPDF_BStr_Set(response, kString, sizeof(kString) - 1);
+ return true;
+}
+
+FPDF_BOOL PutRequestURLStub(FPDF_FORMFILLINFO* pThis,
+ FPDF_WIDESTRING wsURL,
+ FPDF_WIDESTRING wsData,
+ FPDF_WIDESTRING wsEncode) {
+ return true;
+}
+#endif // PDF_ENABLE_XFA
} // namespace
EmbedderTest::EmbedderTest()
- : default_delegate_(pdfium::MakeUnique<EmbedderTest::Delegate>()),
+ : default_delegate_(std::make_unique<EmbedderTest::Delegate>()),
delegate_(default_delegate_.get()) {
FPDF_FILEWRITE::version = 1;
FPDF_FILEWRITE::WriteBlock = WriteBlockCallback;
@@ -64,52 +270,31 @@
EmbedderTest::~EmbedderTest() = default;
void EmbedderTest::SetUp() {
- FPDF_LIBRARY_CONFIG config;
- config.version = 2;
- config.m_pUserFontPaths = nullptr;
- config.m_v8EmbedderSlot = 0;
- config.m_pIsolate = external_isolate_;
- FPDF_InitLibraryWithConfig(&config);
-
UNSUPPORT_INFO* info = static_cast<UNSUPPORT_INFO*>(this);
memset(info, 0, sizeof(UNSUPPORT_INFO));
info->version = 1;
info->FSDK_UnSupport_Handler = UnsupportedHandlerTrampoline;
FSDK_SetUnSpObjProcessHandler(info);
-
- saved_document_ = nullptr;
}
void EmbedderTest::TearDown() {
// Use an EXPECT_EQ() here and continue to let TearDown() finish as cleanly as
- // possible. This can fail when an ASSERT test fails in a test case.
+ // possible. This can fail when an DCHECK test fails in a test case.
EXPECT_EQ(0U, page_map_.size());
EXPECT_EQ(0U, saved_page_map_.size());
-
- if (document_) {
- FORM_DoDocumentAAction(form_handle_, FPDFDOC_AACTION_WC);
+ if (document())
CloseDocument();
- }
-
- FPDFAvail_Destroy(avail_);
- FPDF_DestroyLibrary();
- loader_.reset();
}
-#ifdef PDF_ENABLE_V8
-void EmbedderTest::SetExternalIsolate(void* isolate) {
- external_isolate_ = static_cast<v8::Isolate*>(isolate);
+void EmbedderTest::CreateEmptyDocument() {
+ CreateEmptyDocumentWithoutFormFillEnvironment();
+ form_handle_.reset(SetupFormFillEnvironment(
+ document(), JavaScriptOption::kEnableJavaScript));
}
-#endif // PDF_ENABLE_V8
-bool EmbedderTest::CreateEmptyDocument() {
- document_ = FPDF_CreateNewDocument();
- if (!document_)
- return false;
-
- form_handle_ =
- SetupFormFillEnvironment(document_, JavaScriptOption::kEnableJavaScript);
- return true;
+void EmbedderTest::CreateEmptyDocumentWithoutFormFillEnvironment() {
+ document_.reset(FPDF_CreateNewDocument());
+ DCHECK(document_);
}
bool EmbedderTest::OpenDocument(const std::string& filename) {
@@ -150,7 +335,7 @@
return false;
EXPECT_TRUE(!loader_);
- loader_ = pdfium::MakeUnique<TestLoader>(
+ loader_ = std::make_unique<TestLoader>(
pdfium::make_span(file_contents_.get(), file_length_));
memset(&file_access_, 0, sizeof(file_access_));
@@ -158,7 +343,7 @@
file_access_.m_GetBlock = TestLoader::GetBlock;
file_access_.m_Param = loader_.get();
- fake_file_access_ = pdfium::MakeUnique<FakeFileAccess>(&file_access_);
+ fake_file_access_ = std::make_unique<FakeFileAccess>(&file_access_);
return OpenDocumentHelper(password, linearize_option, javascript_option,
fake_file_access_.get(), &document_, &avail_,
&form_handle_);
@@ -168,42 +353,45 @@
LinearizeOption linearize_option,
JavaScriptOption javascript_option,
FakeFileAccess* network_simulator,
- FPDF_DOCUMENT* document,
- FPDF_AVAIL* avail,
- FPDF_FORMHANDLE* form_handle) {
+ ScopedFPDFDocument* document,
+ ScopedFPDFAvail* avail,
+ ScopedFPDFFormHandle* form_handle) {
network_simulator->AddSegment(0, 1024);
network_simulator->SetRequestedDataAvailable();
- *avail = FPDFAvail_Create(network_simulator->GetFileAvail(),
- network_simulator->GetFileAccess());
- if (FPDFAvail_IsLinearized(*avail) == PDF_LINEARIZED) {
+ avail->reset(FPDFAvail_Create(network_simulator->GetFileAvail(),
+ network_simulator->GetFileAccess()));
+ FPDF_AVAIL avail_ptr = avail->get();
+ FPDF_DOCUMENT document_ptr = nullptr;
+ if (FPDFAvail_IsLinearized(avail_ptr) == PDF_LINEARIZED) {
int32_t nRet = PDF_DATA_NOTAVAIL;
while (nRet == PDF_DATA_NOTAVAIL) {
network_simulator->SetRequestedDataAvailable();
- nRet =
- FPDFAvail_IsDocAvail(*avail, network_simulator->GetDownloadHints());
+ nRet = FPDFAvail_IsDocAvail(avail_ptr,
+ network_simulator->GetDownloadHints());
}
if (nRet == PDF_DATA_ERROR)
return false;
- *document = FPDFAvail_GetDocument(*avail, password);
- if (!*document)
+ document->reset(FPDFAvail_GetDocument(avail_ptr, password));
+ document_ptr = document->get();
+ if (!document_ptr)
return false;
nRet = PDF_DATA_NOTAVAIL;
while (nRet == PDF_DATA_NOTAVAIL) {
network_simulator->SetRequestedDataAvailable();
- nRet =
- FPDFAvail_IsFormAvail(*avail, network_simulator->GetDownloadHints());
+ nRet = FPDFAvail_IsFormAvail(avail_ptr,
+ network_simulator->GetDownloadHints());
}
if (nRet == PDF_FORM_ERROR)
return false;
- int page_count = FPDF_GetPageCount(*document);
+ int page_count = FPDF_GetPageCount(document_ptr);
for (int i = 0; i < page_count; ++i) {
nRet = PDF_DATA_NOTAVAIL;
while (nRet == PDF_DATA_NOTAVAIL) {
network_simulator->SetRequestedDataAvailable();
- nRet = FPDFAvail_IsPageAvail(*avail, i,
+ nRet = FPDFAvail_IsPageAvail(avail_ptr, i,
network_simulator->GetDownloadHints());
}
if (nRet == PDF_DATA_ERROR)
@@ -213,27 +401,31 @@
if (linearize_option == LinearizeOption::kMustLinearize)
return false;
network_simulator->SetWholeFileAvailable();
- *document =
- FPDF_LoadCustomDocument(network_simulator->GetFileAccess(), password);
- if (!*document)
+ document->reset(
+ FPDF_LoadCustomDocument(network_simulator->GetFileAccess(), password));
+ document_ptr = document->get();
+ if (!document_ptr)
return false;
}
- *form_handle = SetupFormFillEnvironment(*document, javascript_option);
+ form_handle->reset(SetupFormFillEnvironment(document_ptr, javascript_option));
- int doc_type = FPDF_GetFormType(*document);
+ int doc_type = FPDF_GetFormType(document_ptr);
if (doc_type == FORMTYPE_XFA_FULL || doc_type == FORMTYPE_XFA_FOREGROUND)
- FPDF_LoadXFA(*document);
+ FPDF_LoadXFA(document_ptr);
- (void)FPDF_GetDocPermissions(*document);
+ (void)FPDF_GetDocPermissions(document_ptr);
return true;
}
void EmbedderTest::CloseDocument() {
- FPDFDOC_ExitFormFillEnvironment(form_handle_);
- form_handle_ = nullptr;
-
- FPDF_CloseDocument(document_);
- document_ = nullptr;
+ FORM_DoDocumentAAction(form_handle(), FPDFDOC_AACTION_WC);
+ form_handle_.reset();
+ document_.reset();
+ avail_.reset();
+ fake_file_access_.reset();
+ memset(&file_access_, 0, sizeof(file_access_));
+ loader_.reset();
+ file_contents_.reset();
}
FPDF_FORMHANDLE EmbedderTest::SetupFormFillEnvironment(
@@ -241,21 +433,46 @@
JavaScriptOption javascript_option) {
IPDF_JSPLATFORM* platform = static_cast<IPDF_JSPLATFORM*>(this);
memset(platform, '\0', sizeof(IPDF_JSPLATFORM));
- platform->version = 2;
+ platform->version = 3;
platform->app_alert = AlertTrampoline;
- platform->m_isolate = external_isolate_;
FPDF_FORMFILLINFO* formfillinfo = static_cast<FPDF_FORMFILLINFO*>(this);
memset(formfillinfo, 0, sizeof(FPDF_FORMFILLINFO));
-#ifdef PDF_ENABLE_XFA
- formfillinfo->version = 2;
-#else // PDF_ENABLE_XFA
- formfillinfo->version = 1;
-#endif // PDF_ENABLE_XFA
+ formfillinfo->version = form_fill_info_version_;
+ formfillinfo->FFI_Invalidate = InvalidateStub;
+ formfillinfo->FFI_OutputSelectedRect = OutputSelectedRectStub;
+ formfillinfo->FFI_SetCursor = SetCursorStub;
formfillinfo->FFI_SetTimer = SetTimerTrampoline;
formfillinfo->FFI_KillTimer = KillTimerTrampoline;
+ formfillinfo->FFI_GetLocalTime = GetLocalTimeStub;
+ formfillinfo->FFI_OnChange = OnChangeStub;
formfillinfo->FFI_GetPage = GetPageTrampoline;
+ formfillinfo->FFI_GetCurrentPage = GetCurrentPageStub;
+ formfillinfo->FFI_GetRotation = GetRotationStub;
+ formfillinfo->FFI_ExecuteNamedAction = ExecuteNamedActionStub;
+ formfillinfo->FFI_SetTextFieldFocus = SetTextFieldFocusStub;
formfillinfo->FFI_DoURIAction = DoURIActionTrampoline;
+ formfillinfo->FFI_DoGoToAction = DoGoToActionTrampoline;
+#ifdef PDF_ENABLE_XFA
+ formfillinfo->FFI_DisplayCaret = DisplayCaretStub;
+ formfillinfo->FFI_GetCurrentPageIndex = GetCurrentPageIndexStub;
+ formfillinfo->FFI_SetCurrentPage = SetCurrentPageStub;
+ formfillinfo->FFI_GotoURL = GotoURLStub;
+ formfillinfo->FFI_GetPageViewRect = GetPageViewRectStub;
+ formfillinfo->FFI_PageEvent = PageEventStub;
+ formfillinfo->FFI_PopupMenu = PopupMenuStub;
+ formfillinfo->FFI_OpenFile = OpenFileStub;
+ formfillinfo->FFI_EmailTo = EmailToStub;
+ formfillinfo->FFI_UploadTo = UploadToStub;
+ formfillinfo->FFI_GetPlatform = GetPlatformStub;
+ formfillinfo->FFI_GetLanguage = GetLanguageStub;
+ formfillinfo->FFI_DownloadFromURL = DownloadFromURLStub;
+ formfillinfo->FFI_PostRequestURL = PostRequestURLStub;
+ formfillinfo->FFI_PutRequestURL = PutRequestURLStub;
+#endif // PDF_ENABLE_XFA
+ formfillinfo->FFI_OnFocusChange = OnFocusChangeTrampoline;
+ formfillinfo->FFI_DoURIActionWithKeyboardModifier =
+ DoURIActionWithKeyboardModifierTrampoline;
if (javascript_option == JavaScriptOption::kEnableJavaScript)
formfillinfo->m_pJsPlatform = platform;
@@ -267,22 +484,22 @@
}
void EmbedderTest::DoOpenActions() {
- ASSERT(form_handle_);
- FORM_DoDocumentJSAction(form_handle_);
- FORM_DoDocumentOpenAction(form_handle_);
+ DCHECK(form_handle());
+ FORM_DoDocumentJSAction(form_handle());
+ FORM_DoDocumentOpenAction(form_handle());
}
int EmbedderTest::GetFirstPageNum() {
- int first_page = FPDFAvail_GetFirstPageNum(document_);
- (void)FPDFAvail_IsPageAvail(avail_, first_page,
+ int first_page = FPDFAvail_GetFirstPageNum(document());
+ (void)FPDFAvail_IsPageAvail(avail(), first_page,
fake_file_access_->GetDownloadHints());
return first_page;
}
int EmbedderTest::GetPageCount() {
- int page_count = FPDF_GetPageCount(document_);
+ int page_count = FPDF_GetPageCount(document());
for (int i = 0; i < page_count; ++i)
- (void)FPDFAvail_IsPageAvail(avail_, i,
+ (void)FPDFAvail_IsPageAvail(avail(), i,
fake_file_access_->GetDownloadHints());
return page_count;
}
@@ -296,17 +513,17 @@
}
FPDF_PAGE EmbedderTest::LoadPageCommon(int page_number, bool do_events) {
- ASSERT(form_handle_);
- ASSERT(page_number >= 0);
- ASSERT(!pdfium::ContainsKey(page_map_, page_number));
+ DCHECK(form_handle());
+ DCHECK(page_number >= 0);
+ DCHECK(!pdfium::Contains(page_map_, page_number));
- FPDF_PAGE page = FPDF_LoadPage(document_, page_number);
+ FPDF_PAGE page = FPDF_LoadPage(document(), page_number);
if (!page)
return nullptr;
if (do_events) {
- FORM_OnAfterLoadPage(page, form_handle_);
- FORM_DoPageAAction(page, form_handle_, FPDFPAGE_AACTION_OPEN);
+ FORM_OnAfterLoadPage(page, form_handle());
+ FORM_DoPageAAction(page, form_handle(), FPDFPAGE_AACTION_OPEN);
}
page_map_[page_number] = page;
return page;
@@ -321,15 +538,15 @@
}
void EmbedderTest::UnloadPageCommon(FPDF_PAGE page, bool do_events) {
- ASSERT(form_handle_);
+ DCHECK(form_handle());
int page_number = GetPageNumberForLoadedPage(page);
if (page_number < 0) {
NOTREACHED();
return;
}
if (do_events) {
- FORM_DoPageAAction(page, form_handle_, FPDFPAGE_AACTION_CLOSE);
- FORM_OnBeforeClosePage(page, form_handle_);
+ FORM_DoPageAAction(page, form_handle(), FPDFPAGE_AACTION_CLOSE);
+ FORM_OnBeforeClosePage(page, form_handle());
}
FPDF_ClosePage(page);
page_map_.erase(page_number);
@@ -350,7 +567,7 @@
NOTREACHED();
return nullptr;
}
- return RenderPageWithFlags(page, form_handle_, flags);
+ return RenderPageWithFlags(page, form_handle(), flags);
}
ScopedFPDFBitmap EmbedderTest::RenderSavedPage(FPDF_PAGE page) {
@@ -363,7 +580,7 @@
NOTREACHED();
return nullptr;
}
- return RenderPageWithFlags(page, saved_form_handle_, flags);
+ return RenderPageWithFlags(page, saved_form_handle(), flags);
}
// static
@@ -386,7 +603,7 @@
return RenderPageWithFlags(page, nullptr, 0);
}
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
// static
std::vector<uint8_t> EmbedderTest::RenderPageWithFlagsToEmf(FPDF_PAGE page,
int flags) {
@@ -406,7 +623,7 @@
FPDF_RenderPage(dc, page, 0, 0, width, height, 0, flags);
HENHMETAFILE emf = CloseEnhMetaFile(dc);
- size_t size_in_bytes = GetEnhMetaFileBits(emf, 0, nullptr);
+ UINT size_in_bytes = GetEnhMetaFileBits(emf, 0, nullptr);
std::vector<uint8_t> buffer(size_in_bytes);
GetEnhMetaFileBits(emf, size_in_bytes, buffer.data());
DeleteEnhMetaFile(emf);
@@ -417,7 +634,8 @@
std::string EmbedderTest::GetPostScriptFromEmf(
pdfium::span<const uint8_t> emf_data) {
// This comes from Emf::InitFromData() in Chromium.
- HENHMETAFILE emf = SetEnhMetaFileBits(emf_data.size(), emf_data.data());
+ HENHMETAFILE emf = SetEnhMetaFileBits(
+ pdfium::base::checked_cast<UINT>(emf_data.size()), emf_data.data());
if (!emf)
return std::string();
@@ -446,7 +664,7 @@
DeleteEnhMetaFile(emf);
return ps_data;
}
-#endif // defined(OS_WIN)
+#endif // BUILDFLAG(IS_WIN)
FPDF_DOCUMENT EmbedderTest::OpenSavedDocument() {
return OpenSavedDocumentWithPassword(nullptr);
@@ -471,51 +689,48 @@
FPDF_DOCUMENT EmbedderTest::OpenSavedDocumentWithPassword(
const char* password) {
memset(&saved_file_access_, 0, sizeof(saved_file_access_));
- saved_file_access_.m_FileLen = data_string_.size();
+ saved_file_access_.m_FileLen =
+ pdfium::base::checked_cast<unsigned long>(data_string_.size());
saved_file_access_.m_GetBlock = GetBlockFromString;
// Copy data to prevent clearing it before saved document close.
saved_document_file_data_ = data_string_;
saved_file_access_.m_Param = &saved_document_file_data_;
saved_fake_file_access_ =
- pdfium::MakeUnique<FakeFileAccess>(&saved_file_access_);
+ std::make_unique<FakeFileAccess>(&saved_file_access_);
EXPECT_TRUE(OpenDocumentHelper(
password, LinearizeOption::kDefaultLinearize,
JavaScriptOption::kEnableJavaScript, saved_fake_file_access_.get(),
&saved_document_, &saved_avail_, &saved_form_handle_));
- return saved_document_;
+ return saved_document();
}
void EmbedderTest::CloseSavedDocument() {
- ASSERT(saved_document_);
+ DCHECK(saved_document());
- FPDFDOC_ExitFormFillEnvironment(saved_form_handle_);
- FPDF_CloseDocument(saved_document_);
- FPDFAvail_Destroy(saved_avail_);
-
- saved_form_handle_ = nullptr;
- saved_document_ = nullptr;
- saved_avail_ = nullptr;
+ saved_form_handle_.reset();
+ saved_document_.reset();
+ saved_avail_.reset();
}
FPDF_PAGE EmbedderTest::LoadSavedPage(int page_number) {
- ASSERT(saved_form_handle_);
- ASSERT(page_number >= 0);
- ASSERT(!pdfium::ContainsKey(saved_page_map_, page_number));
+ DCHECK(saved_form_handle());
+ DCHECK(page_number >= 0);
+ DCHECK(!pdfium::Contains(saved_page_map_, page_number));
- FPDF_PAGE page = FPDF_LoadPage(saved_document_, page_number);
+ FPDF_PAGE page = FPDF_LoadPage(saved_document(), page_number);
if (!page)
return nullptr;
- FORM_OnAfterLoadPage(page, saved_form_handle_);
- FORM_DoPageAAction(page, saved_form_handle_, FPDFPAGE_AACTION_OPEN);
+ FORM_OnAfterLoadPage(page, saved_form_handle());
+ FORM_DoPageAAction(page, saved_form_handle(), FPDFPAGE_AACTION_OPEN);
saved_page_map_[page_number] = page;
return page;
}
void EmbedderTest::CloseSavedPage(FPDF_PAGE page) {
- ASSERT(saved_form_handle_);
+ DCHECK(saved_form_handle());
int page_number = GetPageNumberForSavedPage(page);
if (page_number < 0) {
@@ -523,8 +738,8 @@
return;
}
- FORM_DoPageAAction(page, saved_form_handle_, FPDFPAGE_AACTION_CLOSE);
- FORM_OnBeforeClosePage(page, saved_form_handle_);
+ FORM_DoPageAAction(page, saved_form_handle(), FPDFPAGE_AACTION_CLOSE);
+ FORM_OnBeforeClosePage(page, saved_form_handle());
FPDF_ClosePage(page);
saved_page_map_.erase(page_number);
@@ -534,8 +749,8 @@
int width,
int height,
const char* md5) {
- ASSERT(saved_document_);
- ASSERT(page);
+ DCHECK(saved_document());
+ DCHECK(page);
ScopedFPDFBitmap bitmap = RenderSavedPageWithFlags(page, FPDF_ANNOT);
CompareBitmap(bitmap.get(), width, height, md5);
@@ -550,10 +765,19 @@
}
void EmbedderTest::SetWholeFileAvailable() {
- ASSERT(fake_file_access_);
+ DCHECK(fake_file_access_);
fake_file_access_->SetWholeFileAvailable();
}
+void EmbedderTest::SetDocumentFromAvail() {
+ document_.reset(FPDFAvail_GetDocument(avail(), nullptr));
+}
+
+void EmbedderTest::CreateAvail(FX_FILEAVAIL* file_avail,
+ FPDF_FILEACCESS* file) {
+ avail_.reset(FPDFAvail_Create(file_avail, file));
+}
+
FPDF_PAGE EmbedderTest::Delegate::GetPage(FPDF_FORMFILLINFO* info,
FPDF_DOCUMENT document,
int page_index) {
@@ -563,52 +787,6 @@
}
// static
-void EmbedderTest::UnsupportedHandlerTrampoline(UNSUPPORT_INFO* info,
- int type) {
- EmbedderTest* test = static_cast<EmbedderTest*>(info);
- test->delegate_->UnsupportedHandler(type);
-}
-
-// static
-int EmbedderTest::AlertTrampoline(IPDF_JSPLATFORM* platform,
- FPDF_WIDESTRING message,
- FPDF_WIDESTRING title,
- int type,
- int icon) {
- EmbedderTest* test = static_cast<EmbedderTest*>(platform);
- return test->delegate_->Alert(message, title, type, icon);
-}
-
-// static
-int EmbedderTest::SetTimerTrampoline(FPDF_FORMFILLINFO* info,
- int msecs,
- TimerCallback fn) {
- EmbedderTest* test = static_cast<EmbedderTest*>(info);
- return test->delegate_->SetTimer(msecs, fn);
-}
-
-// static
-void EmbedderTest::KillTimerTrampoline(FPDF_FORMFILLINFO* info, int id) {
- EmbedderTest* test = static_cast<EmbedderTest*>(info);
- return test->delegate_->KillTimer(id);
-}
-
-// static
-FPDF_PAGE EmbedderTest::GetPageTrampoline(FPDF_FORMFILLINFO* info,
- FPDF_DOCUMENT document,
- int page_index) {
- return static_cast<EmbedderTest*>(info)->delegate_->GetPage(info, document,
- page_index);
-}
-
-// static
-void EmbedderTest::DoURIActionTrampoline(FPDF_FORMFILLINFO* info,
- FPDF_BYTESTRING uri) {
- EmbedderTest* test = static_cast<EmbedderTest*>(info);
- return test->delegate_->DoURIAction(uri);
-}
-
-// static
std::string EmbedderTest::HashBitmap(FPDF_BITMAP bitmap) {
int stride = FPDFBitmap_GetStride(bitmap);
int usable_bytes_per_row =
@@ -625,13 +803,11 @@
return CryptToBase16(digest);
}
-#ifndef NDEBUG
// static
void EmbedderTest::WriteBitmapToPng(FPDF_BITMAP bitmap,
const std::string& filename) {
BitmapSaver::WriteBitmapToPng(bitmap, filename);
}
-#endif
// static
void EmbedderTest::CompareBitmap(FPDF_BITMAP bitmap,
@@ -650,7 +826,11 @@
if (!expected_md5sum)
return;
- EXPECT_EQ(expected_md5sum, HashBitmap(bitmap));
+ std::string actual_md5sum = HashBitmap(bitmap);
+ EXPECT_EQ(expected_md5sum, actual_md5sum);
+ if (EmbedderTestEnvironment::GetInstance()->write_pngs()) {
+ WriteBitmapToPng(bitmap, actual_md5sum + ".png");
+ }
}
// static
@@ -693,7 +873,7 @@
for (const auto& it : page_map) {
if (it.second == page) {
int page_number = it.first;
- ASSERT(page_number >= 0);
+ DCHECK(page_number >= 0);
return page_number;
}
}
diff --git a/testing/embedder_test.h b/testing/embedder_test.h
index 662333c..c73dd61 100644
--- a/testing/embedder_test.h
+++ b/testing/embedder_test.h
@@ -1,4 +1,4 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -25,6 +25,10 @@
class TestLoader;
+// The loading time of the CFGAS_FontMgr is linear in the number of times it is
+// loaded. So, if a test suite has a lot of tests that need a font manager they
+// can end up executing very, very slowly.
+
// This class is used to load a PDF document, and then run programatic
// API tests against it.
class EmbedderTest : public ::testing::Test,
@@ -64,6 +68,23 @@
// Equivalent to FPDF_FORMFILLINFO::FFI_DoURIAction().
virtual void DoURIAction(FPDF_BYTESTRING uri) {}
+
+ // Equivalent to FPDF_FORMFILLINFO::FFI_DoGoToAction().
+ virtual void DoGoToAction(FPDF_FORMFILLINFO* info,
+ int page_index,
+ int zoom_mode,
+ float* pos_arry,
+ int array_size) {}
+
+ // Equivalent to FPDF_FORMFILLINFO::FFI_OnFocusChange().
+ virtual void OnFocusChange(FPDF_FORMFILLINFO* info,
+ FPDF_ANNOTATION annot,
+ int page_index) {}
+
+ // Equivalent to FPDF_FORMFILLINFO::FFI_DoURIActionWithKeyboardModifier().
+ virtual void DoURIActionWithKeyboardModifier(FPDF_FORMFILLINFO* info,
+ FPDF_BYTESTRING uri,
+ int modifiers) {}
};
EmbedderTest();
@@ -72,21 +93,30 @@
void SetUp() override;
void TearDown() override;
-#ifdef PDF_ENABLE_V8
- // Call before SetUp to pass shared isolate, otherwise PDFium creates one.
- void SetExternalIsolate(void* isolate);
-#endif // PDF_ENABLE_V8
-
+ Delegate* GetDelegate() { return delegate_; }
void SetDelegate(Delegate* delegate) {
delegate_ = delegate ? delegate : default_delegate_.get();
}
- FPDF_DOCUMENT document() const { return document_; }
- FPDF_FORMHANDLE form_handle() const { return form_handle_; }
+ void SetFormFillInfoVersion(int form_fill_info_version) {
+ form_fill_info_version_ = form_fill_info_version;
+ }
- // Create an empty document, and its form fill environment. Returns true
- // on success or false on failure.
- bool CreateEmptyDocument();
+ void SetDocumentFromAvail();
+ FPDF_DOCUMENT document() const { return document_.get(); }
+ FPDF_DOCUMENT saved_document() const { return saved_document_.get(); }
+ FPDF_FORMHANDLE form_handle() const { return form_handle_.get(); }
+ FPDF_FORMHANDLE saved_form_handle() const { return saved_form_handle_.get(); }
+
+ // Wrapper for FPDFAvail_Create() to set `avail_`.
+ void CreateAvail(FX_FILEAVAIL* file_avail, FPDF_FILEACCESS* file);
+ FPDF_AVAIL avail() { return avail_.get(); }
+
+ // Create an empty document, and its form fill environment.
+ void CreateEmptyDocument();
+
+ // Create an empty document without a form fill environment.
+ void CreateEmptyDocumentWithoutFormFillEnvironment();
// Open the document specified by |filename|, and create its form fill
// environment, or return false on failure. The |filename| is relative to
@@ -171,14 +201,14 @@
// Simplified form of RenderPageWithFlags() with no handle and no flags.
static ScopedFPDFBitmap RenderPage(FPDF_PAGE page);
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
// Convert |page| into EMF with the specified page rendering |flags|.
static std::vector<uint8_t> RenderPageWithFlagsToEmf(FPDF_PAGE page,
int flags);
// Get the PostScript data from |emf_data|.
static std::string GetPostScriptFromEmf(pdfium::span<const uint8_t> emf_data);
-#endif // defined(OS_WIN)
+#endif // BUILDFLAG(IS_WIN)
// Return bytes for each of the FPDFBitmap_* format types.
static int BytesPerPixelForFormat(int format);
@@ -190,9 +220,9 @@
LinearizeOption linearize_option,
JavaScriptOption javascript_option,
FakeFileAccess* network_simulator,
- FPDF_DOCUMENT* document,
- FPDF_AVAIL* avail,
- FPDF_FORMHANDLE* form_handle);
+ ScopedFPDFDocument* document,
+ ScopedFPDFAvail* avail,
+ ScopedFPDFFormHandle* form_handle);
FPDF_FORMHANDLE SetupFormFillEnvironment(FPDF_DOCUMENT doc,
JavaScriptOption javascript_option);
@@ -201,11 +231,9 @@
// any, at the end of a row where the stride is larger than width * bpp.
static std::string HashBitmap(FPDF_BITMAP bitmap);
-#ifndef NDEBUG
// For debugging purposes.
// Write |bitmap| as a PNG to |filename|.
static void WriteBitmapToPng(FPDF_BITMAP bitmap, const std::string& filename);
-#endif
// Check |bitmap| to make sure it has the right dimensions and content.
static void CompareBitmap(FPDF_BITMAP bitmap,
@@ -245,45 +273,7 @@
void ClosePDFFileForWrite();
#endif
- std::unique_ptr<Delegate> default_delegate_;
- Delegate* delegate_;
-
- FPDF_DOCUMENT document_ = nullptr;
- FPDF_FORMHANDLE form_handle_ = nullptr;
- FPDF_AVAIL avail_ = nullptr;
- FPDF_FILEACCESS file_access_; // must outlive |avail_|.
- std::unique_ptr<FakeFileAccess> fake_file_access_; // must outlive |avail_|.
-
- void* external_isolate_ = nullptr;
- std::unique_ptr<TestLoader> loader_;
- size_t file_length_ = 0;
- std::unique_ptr<char, pdfium::FreeDeleter> file_contents_;
- PageNumberToHandleMap page_map_;
-
- FPDF_DOCUMENT saved_document_ = nullptr;
- FPDF_FORMHANDLE saved_form_handle_ = nullptr;
- FPDF_AVAIL saved_avail_ = nullptr;
- FPDF_FILEACCESS saved_file_access_; // must outlive |saved_avail_|.
- // must outlive |saved_avail_|.
- std::unique_ptr<FakeFileAccess> saved_fake_file_access_;
- PageNumberToHandleMap saved_page_map_;
-
private:
- static void UnsupportedHandlerTrampoline(UNSUPPORT_INFO*, int type);
- static int AlertTrampoline(IPDF_JSPLATFORM* plaform,
- FPDF_WIDESTRING message,
- FPDF_WIDESTRING title,
- int type,
- int icon);
- static int SetTimerTrampoline(FPDF_FORMFILLINFO* info,
- int msecs,
- TimerCallback fn);
- static void KillTimerTrampoline(FPDF_FORMFILLINFO* info, int id);
- static FPDF_PAGE GetPageTrampoline(FPDF_FORMFILLINFO* info,
- FPDF_DOCUMENT document,
- int page_index);
- static void DoURIActionTrampoline(FPDF_FORMFILLINFO* info,
- FPDF_BYTESTRING uri);
static int WriteBlockCallback(FPDF_FILEWRITE* pFileWrite,
const void* data,
unsigned long size);
@@ -301,6 +291,34 @@
void UnloadPageCommon(FPDF_PAGE page, bool do_events);
FPDF_PAGE LoadPageCommon(int page_number, bool do_events);
+ std::unique_ptr<Delegate> default_delegate_;
+ Delegate* delegate_;
+
+#ifdef PDF_ENABLE_XFA
+ int form_fill_info_version_ = 2;
+#else // PDF_ENABLE_XFA
+ int form_fill_info_version_ = 1;
+#endif // PDF_ENABLE_XFA
+
+ size_t file_length_ = 0;
+ // must outlive `loader_`.
+ std::unique_ptr<char, pdfium::FreeDeleter> file_contents_;
+ std::unique_ptr<TestLoader> loader_;
+ FPDF_FILEACCESS file_access_; // must outlive `avail_`.
+ std::unique_ptr<FakeFileAccess> fake_file_access_; // must outlive `avail_`.
+ ScopedFPDFAvail avail_;
+ ScopedFPDFDocument document_;
+ ScopedFPDFFormHandle form_handle_;
+ PageNumberToHandleMap page_map_;
+
+ FPDF_FILEACCESS saved_file_access_; // must outlive `saved_avail_`.
+ // must outlive `saved_avail_`.
+ std::unique_ptr<FakeFileAccess> saved_fake_file_access_;
+ ScopedFPDFAvail saved_avail_;
+ ScopedFPDFDocument saved_document_;
+ ScopedFPDFFormHandle saved_form_handle_;
+ PageNumberToHandleMap saved_page_map_;
+
std::string data_string_;
std::string saved_document_file_data_;
std::ofstream filestream_;
diff --git a/testing/embedder_test_constants.cpp b/testing/embedder_test_constants.cpp
new file mode 100644
index 0000000..9e8caee
--- /dev/null
+++ b/testing/embedder_test_constants.cpp
@@ -0,0 +1,68 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/embedder_test_constants.h"
+
+#include "build/build_config.h"
+#include "core/fxge/cfx_defaultrenderdevice.h"
+
+namespace pdfium {
+
+const char* AnnotationStampWithApChecksum() {
+ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+ return "a31381406d0b95049e418720750b78dd";
+#if BUILDFLAG(IS_APPLE)
+ return "0521eaa52fe2aa43aafd3e4495f63f0b";
+#else
+ return "5f19ddad9d48f5b7b87ee7d92f577db6";
+#endif
+}
+
+const char kBlankPage612By792Checksum[] = "1940568c9ba33bac5d0b1ee9558c76b3";
+
+const char* Bug890322Checksum() {
+ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+ return "793689536cf64fe792c2f241888c0cf3";
+ return "6c674642154408e877d88c6c082d67e9";
+}
+
+const char* HelloWorldChecksum() {
+#if BUILDFLAG(IS_APPLE)
+ if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+ return "6eef7237f7591f07616e238422086737";
+#endif
+ return "c1c548442e0e0f949c5550d89bf8ae3b";
+}
+
+const char* HelloWorldRemovedChecksum() {
+#if BUILDFLAG(IS_APPLE)
+ if (!CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+ return "6e1cae48a2e35c521dee4ca502f48af6";
+#endif
+ return "4a9b80f675f7f3bf2da1b02f12449e4b";
+}
+
+const char* ManyRectanglesChecksum() {
+ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+ return "4e7e280c1597222afcb0ee3bb90ec119";
+ return "b0170c575b65ecb93ebafada0ff0f038";
+}
+
+const char* RectanglesChecksum() {
+ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+ return "b4e411a6b5ffa59a50efede2efece597";
+ return "0a90de37f52127619c3dfb642b5fa2fe";
+}
+
+const char* TextFormChecksum() {
+ if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer())
+ return "e6d2eb75f18d773f0dad938b1bb22e23";
+#if BUILDFLAG(IS_APPLE)
+ return "fa2bf756942a950101fc147fc4ef3f82";
+#else
+ return "6f86fe1dbed5965d91aec6e0b829e29f";
+#endif
+}
+
+} // namespace pdfium
diff --git a/testing/embedder_test_constants.h b/testing/embedder_test_constants.h
new file mode 100644
index 0000000..306880b
--- /dev/null
+++ b/testing/embedder_test_constants.h
@@ -0,0 +1,36 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TESTING_EMBEDDER_TEST_CONSTANTS_H_
+#define TESTING_EMBEDDER_TEST_CONSTANTS_H_
+
+namespace pdfium {
+
+// MD5 hash for rendering annotation_stamp_with_ap.pdf with annotations.
+const char* AnnotationStampWithApChecksum();
+
+// MD5 hash for rendering a 612x792 blank page.
+extern const char kBlankPage612By792Checksum[];
+
+// MD5 hash for rendering bug_890322.pdf.
+const char* Bug890322Checksum();
+
+// MD5 hash for rendering hello_world.pdf or bug_455199.pdf.
+const char* HelloWorldChecksum();
+
+// MD5 hash for rendering hello_world.pdf after removing "Goodbye, world!".
+const char* HelloWorldRemovedChecksum();
+
+// MD5 hash for rendering many_rectangles.pdf.
+const char* ManyRectanglesChecksum();
+
+// MD5 hash for rendering rectangles.pdf.
+const char* RectanglesChecksum();
+
+// MD5 hash for rendering text_form.pdf.
+const char* TextFormChecksum();
+
+} // namespace pdfium
+
+#endif // TESTING_EMBEDDER_TEST_CONSTANTS_H_
diff --git a/testing/embedder_test_environment.cpp b/testing/embedder_test_environment.cpp
new file mode 100644
index 0000000..07e459a
--- /dev/null
+++ b/testing/embedder_test_environment.cpp
@@ -0,0 +1,93 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/embedder_test_environment.h"
+
+#include <ostream>
+
+#include "core/fxcrt/fx_system.h"
+#include "public/fpdfview.h"
+#include "testing/command_line_helpers.h"
+#include "third_party/base/check.h"
+
+#ifdef PDF_ENABLE_V8
+#include "testing/v8_test_environment.h"
+#endif // PDF_ENABLE_V8
+
+namespace {
+
+EmbedderTestEnvironment* g_environment = nullptr;
+
+} // namespace
+
+EmbedderTestEnvironment::EmbedderTestEnvironment()
+ : renderer_type_(GetDefaultRendererType()) {
+ DCHECK(!g_environment);
+ g_environment = this;
+}
+
+EmbedderTestEnvironment::~EmbedderTestEnvironment() {
+ DCHECK(g_environment);
+ g_environment = nullptr;
+}
+
+// static
+EmbedderTestEnvironment* EmbedderTestEnvironment::GetInstance() {
+ return g_environment;
+}
+
+void EmbedderTestEnvironment::SetUp() {
+ FPDF_LIBRARY_CONFIG config;
+ config.version = 4;
+ config.m_pUserFontPaths = nullptr;
+ config.m_v8EmbedderSlot = 0;
+ config.m_pPlatform = nullptr;
+
+ config.m_pUserFontPaths = test_fonts_.font_paths();
+
+#ifdef PDF_ENABLE_V8
+ config.m_pIsolate = V8TestEnvironment::GetInstance()->isolate();
+ config.m_pPlatform = V8TestEnvironment::GetInstance()->platform();
+#else // PDF_ENABLE_V8
+ config.m_pIsolate = nullptr;
+ config.m_pPlatform = nullptr;
+#endif // PDF_ENABLE_V8
+ config.m_RendererType = renderer_type_;
+
+ FPDF_InitLibraryWithConfig(&config);
+
+ test_fonts_.InstallFontMapper();
+}
+
+void EmbedderTestEnvironment::TearDown() {
+ FPDF_DestroyLibrary();
+}
+
+void EmbedderTestEnvironment::AddFlags(int argc, char** argv) {
+ for (int i = 1; i < argc; ++i)
+ AddFlag(argv[i]);
+}
+
+void EmbedderTestEnvironment::AddFlag(const std::string& flag) {
+ if (flag == "--write-pngs") {
+ write_pngs_ = true;
+ return;
+ }
+#if defined(_SKIA_SUPPORT_)
+ std::string value;
+ if (ParseSwitchKeyValue(flag, "--use-renderer=", &value)) {
+ if (value == "agg") {
+ renderer_type_ = FPDF_RENDERERTYPE_AGG;
+ } else if (value == "skia") {
+ renderer_type_ = FPDF_RENDERERTYPE_SKIA;
+ } else {
+ std::cerr << "Invalid --use-renderer argument, value must be one of agg "
+ "or skia\n";
+ }
+ return;
+ }
+#endif // defined(_SKIA_SUPPORT_)
+
+ std::cerr << "Unknown flag: " << flag << "\n";
+}
diff --git a/testing/embedder_test_environment.h b/testing/embedder_test_environment.h
new file mode 100644
index 0000000..e04c5ae
--- /dev/null
+++ b/testing/embedder_test_environment.h
@@ -0,0 +1,39 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TESTING_EMBEDDER_TEST_ENVIRONMENT_H_
+#define TESTING_EMBEDDER_TEST_ENVIRONMENT_H_
+
+#include <string>
+
+#include "public/fpdfview.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/test_fonts.h"
+
+class EmbedderTestEnvironment : public testing::Environment {
+ public:
+ EmbedderTestEnvironment();
+ ~EmbedderTestEnvironment() override;
+
+ // Note: GetInstance() does not create one if it does not exist,
+ // so the main program must explicitly add this enviroment.
+ static EmbedderTestEnvironment* GetInstance();
+
+ // testing::Environment:
+ void SetUp() override;
+ void TearDown() override;
+
+ void AddFlags(int argc, char** argv);
+
+ bool write_pngs() const { return write_pngs_; }
+
+ private:
+ void AddFlag(const std::string& flag);
+
+ FPDF_RENDERER_TYPE renderer_type_;
+ bool write_pngs_ = false;
+ TestFonts test_fonts_;
+};
+
+#endif // TESTING_EMBEDDER_TEST_ENVIRONMENT_H_
diff --git a/testing/embedder_test_main.cpp b/testing/embedder_test_main.cpp
index 34dfb06..53049b6 100644
--- a/testing/embedder_test_main.cpp
+++ b/testing/embedder_test_main.cpp
@@ -1,90 +1,36 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include <memory>
-#include <string>
-
+#include "build/build_config.h"
#include "core/fxcrt/fx_memory.h"
+#include "testing/embedder_test_environment.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#ifdef PDF_ENABLE_V8
-#include "testing/v8_initializer.h"
-#include "v8/include/v8-platform.h"
-#include "v8/include/v8.h"
-#endif // PDF_ENABLE_v8
-
-namespace {
-
-const char* g_exe_path = nullptr;
-
-#ifdef PDF_ENABLE_V8
-#ifdef V8_USE_EXTERNAL_STARTUP_DATA
-v8::StartupData* g_v8_snapshot = nullptr;
-#endif // V8_USE_EXTERNAL_STARTUP_DATA
+#include "testing/v8_test_environment.h"
#endif // PDF_ENABLE_V8
-// The loading time of the CFGAS_FontMgr is linear in the number of times it is
-// loaded. So, if a test suite has a lot of tests that need a font manager they
-// can end up executing very, very slowly.
-class Environment final : public testing::Environment {
- public:
- void SetUp() override {
-#ifdef PDF_ENABLE_V8
-#ifdef V8_USE_EXTERNAL_STARTUP_DATA
- if (g_v8_snapshot) {
- platform_ = InitializeV8ForPDFiumWithStartupData(g_exe_path,
- std::string(), nullptr);
- } else {
- g_v8_snapshot = new v8::StartupData;
- platform_ = InitializeV8ForPDFiumWithStartupData(
- g_exe_path, std::string(), g_v8_snapshot);
- }
-#else
- platform_ = InitializeV8ForPDFium(g_exe_path);
-#endif // V8_USE_EXTERNAL_STARTUP_DATA
-#endif // FPDF_ENABLE_V8
- }
-
- void TearDown() override {
-#ifdef PDF_ENABLE_V8
- v8::V8::ShutdownPlatform();
-#endif // PDF_ENABLE_V8
- }
-
- private:
-#ifdef PDF_ENABLE_V8
- std::unique_ptr<v8::Platform> platform_;
-#endif // PDF_ENABLE_V8
-};
-
-Environment* env_ = nullptr;
-
-} // namespace
-
-// Can't use gtest-provided main since we need to stash the path to the
-// executable in order to find the external V8 binary data files.
+// Can't use gtest-provided main since we need to create our own
+// testing environment which needs the executable path in order to
+// find the external V8 binary data files.
int main(int argc, char** argv) {
- g_exe_path = argv[0];
+ FX_InitializeMemoryAllocators();
- FXMEM_InitializePartitionAlloc();
-
- env_ = new Environment();
+#ifdef PDF_ENABLE_V8
// The env will be deleted by gtest.
- AddGlobalTestEnvironment(env_);
+ AddGlobalTestEnvironment(new V8TestEnvironment(argv[0]));
+#endif // PDF_ENABLE_V8
+
+ // The env will be deleted by gtest.
+ AddGlobalTestEnvironment(new EmbedderTestEnvironment);
testing::InitGoogleTest(&argc, argv);
testing::InitGoogleMock(&argc, argv);
- int ret_val = RUN_ALL_TESTS();
+ // Anything remaining in argc/argv is an embedder_tests flag.
+ EmbedderTestEnvironment::GetInstance()->AddFlags(argc, argv);
-#ifdef PDF_ENABLE_V8
-#ifdef V8_USE_EXTERNAL_STARTUP_DATA
- if (g_v8_snapshot)
- free(const_cast<char*>(g_v8_snapshot->data));
-#endif // V8_USE_EXTERNAL_STARTUP_DATA
-#endif // PDF_ENABLE_V8
-
- return ret_val;
+ return RUN_ALL_TESTS();
}
diff --git a/testing/embedder_test_mock_delegate.h b/testing/embedder_test_mock_delegate.h
index c3f2820..7309541 100644
--- a/testing/embedder_test_mock_delegate.h
+++ b/testing/embedder_test_mock_delegate.h
@@ -1,4 +1,4 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -8,7 +8,7 @@
#include "testing/embedder_test.h"
#include "testing/gmock/include/gmock/gmock.h"
-class EmbedderTestMockDelegate final : public EmbedderTest::Delegate {
+class EmbedderTestMockDelegate : public EmbedderTest::Delegate {
public:
MOCK_METHOD1(UnsupportedHandler, void(int type));
MOCK_METHOD4(
@@ -16,6 +16,21 @@
int(FPDF_WIDESTRING message, FPDF_WIDESTRING title, int type, int icon));
MOCK_METHOD2(SetTimer, int(int msecs, TimerCallback fn));
MOCK_METHOD1(KillTimer, void(int msecs));
+ MOCK_METHOD1(DoURIAction, void(FPDF_BYTESTRING uri));
+ MOCK_METHOD5(DoGoToAction,
+ void(FPDF_FORMFILLINFO* info,
+ int page_index,
+ int zoom_mode,
+ float* pos_array,
+ int array_size));
+ MOCK_METHOD3(OnFocusChange,
+ void(FPDF_FORMFILLINFO* info,
+ FPDF_ANNOTATION annot,
+ int page_index));
+ MOCK_METHOD3(DoURIActionWithKeyboardModifier,
+ void(FPDF_FORMFILLINFO* info,
+ FPDF_BYTESTRING uri,
+ int modifiers));
};
#endif // TESTING_EMBEDDER_TEST_MOCK_DELEGATE_H_
diff --git a/testing/embedder_test_timer_handling_delegate.h b/testing/embedder_test_timer_handling_delegate.h
index a32ad20..e511254 100644
--- a/testing/embedder_test_timer_handling_delegate.h
+++ b/testing/embedder_test_timer_handling_delegate.h
@@ -1,4 +1,4 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -57,7 +57,7 @@
void AdvanceTime(int increment_msecs) {
fake_elapsed_msecs_ += increment_msecs;
- while (1) {
+ while (true) {
auto iter = expiry_to_timer_map_.begin();
if (iter == expiry_to_timer_map_.end()) {
break;
diff --git a/testing/external_engine_embedder_test.cpp b/testing/external_engine_embedder_test.cpp
new file mode 100644
index 0000000..031981d
--- /dev/null
+++ b/testing/external_engine_embedder_test.cpp
@@ -0,0 +1,37 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/external_engine_embedder_test.h"
+
+#include <memory>
+
+#include "fxjs/cfxjs_engine.h"
+#include "testing/v8_test_environment.h"
+#include "v8/include/v8-context.h"
+#include "v8/include/v8-isolate.h"
+#include "v8/include/v8-local-handle.h"
+
+ExternalEngineEmbedderTest::ExternalEngineEmbedderTest() = default;
+
+ExternalEngineEmbedderTest::~ExternalEngineEmbedderTest() = default;
+
+void ExternalEngineEmbedderTest::SetUp() {
+ EmbedderTest::SetUp();
+
+ v8::Isolate::Scope isolate_scope(isolate());
+ v8::HandleScope handle_scope(isolate());
+ FXJS_PerIsolateData::SetUp(isolate());
+ m_Engine = std::make_unique<CFXJS_Engine>(isolate());
+ m_Engine->InitializeEngine();
+}
+
+void ExternalEngineEmbedderTest::TearDown() {
+ m_Engine->ReleaseEngine();
+ m_Engine.reset();
+ JSEmbedderTest::TearDown();
+}
+
+v8::Local<v8::Context> ExternalEngineEmbedderTest::GetV8Context() {
+ return m_Engine->GetV8Context();
+}
diff --git a/testing/external_engine_embedder_test.h b/testing/external_engine_embedder_test.h
new file mode 100644
index 0000000..258e124
--- /dev/null
+++ b/testing/external_engine_embedder_test.h
@@ -0,0 +1,35 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TESTING_EXTERNAL_ENGINE_EMBEDDER_TEST_H_
+#define TESTING_EXTERNAL_ENGINE_EMBEDDER_TEST_H_
+
+#include <memory>
+
+#include "testing/js_embedder_test.h"
+#include "v8/include/v8-context.h"
+#include "v8/include/v8-local-handle.h"
+
+class CFXJS_Engine;
+
+// Test class that allows creating a FXJS javascript engine without
+// first having to load a document and instantiate a form filler
+// against it. Generally, most tests will want to do the latter.
+class ExternalEngineEmbedderTest : public JSEmbedderTest {
+ public:
+ ExternalEngineEmbedderTest();
+ ~ExternalEngineEmbedderTest() override;
+
+ // EmbedderTest:
+ void SetUp() override;
+ void TearDown() override;
+
+ CFXJS_Engine* engine() const { return m_Engine.get(); }
+ v8::Local<v8::Context> GetV8Context();
+
+ private:
+ std::unique_ptr<CFXJS_Engine> m_Engine;
+};
+
+#endif // TESTING_EXTERNAL_ENGINE_EMBEDDER_TEST_H_
diff --git a/testing/fake_file_access.cpp b/testing/fake_file_access.cpp
index 723f772..4bd8fa1 100644
--- a/testing/fake_file_access.cpp
+++ b/testing/fake_file_access.cpp
@@ -1,16 +1,13 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "testing/fake_file_access.h"
-#include <algorithm>
-#include <set>
#include <utility>
-#include <vector>
#include "core/fxcrt/fx_system.h"
-#include "third_party/base/ptr_util.h"
+#include "third_party/base/check.h"
namespace {
@@ -76,13 +73,13 @@
FakeFileAccess::FakeFileAccess(FPDF_FILEACCESS* file_access)
: file_access_(file_access),
- file_access_wrapper_(pdfium::MakeUnique<FileAccessWrapper>(this)),
- file_avail_(pdfium::MakeUnique<FileAvailImpl>(this)),
- download_hints_(pdfium::MakeUnique<DownloadHintsImpl>(this)) {
- ASSERT(file_access_);
+ file_access_wrapper_(std::make_unique<FileAccessWrapper>(this)),
+ file_avail_(std::make_unique<FileAvailImpl>(this)),
+ download_hints_(std::make_unique<DownloadHintsImpl>(this)) {
+ DCHECK(file_access_);
}
-FakeFileAccess::~FakeFileAccess() {}
+FakeFileAccess::~FakeFileAccess() = default;
FPDF_BOOL FakeFileAccess::IsDataAvail(size_t offset, size_t size) const {
return available_data_.Contains(RangeSet::Range(offset, offset + size));
diff --git a/testing/fake_file_access.h b/testing/fake_file_access.h
index c8c08e3..6a0f3cf 100644
--- a/testing/fake_file_access.h
+++ b/testing/fake_file_access.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
diff --git a/testing/font_renamer.cpp b/testing/font_renamer.cpp
new file mode 100644
index 0000000..6a3a81c
--- /dev/null
+++ b/testing/font_renamer.cpp
@@ -0,0 +1,91 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/font_renamer.h"
+
+#include <string>
+
+#include "testing/test_fonts.h"
+
+namespace {
+
+FPDF_SYSFONTINFO* GetImpl(FPDF_SYSFONTINFO* info) {
+ return static_cast<FontRenamer*>(info)->impl();
+}
+
+void ReleaseImpl(FPDF_SYSFONTINFO* info) {
+ FPDF_SYSFONTINFO* impl = GetImpl(info);
+ impl->Release(impl);
+}
+
+void EnumFontsImpl(FPDF_SYSFONTINFO* info, void* mapper) {
+ FPDF_SYSFONTINFO* impl = GetImpl(info);
+ impl->EnumFonts(impl, mapper);
+}
+
+void* MapFontImpl(FPDF_SYSFONTINFO* info,
+ int weight,
+ FPDF_BOOL italic,
+ int charset,
+ int pitch_family,
+ const char* face,
+ FPDF_BOOL* exact) {
+ std::string renamed_face = TestFonts::RenameFont(face);
+ FPDF_SYSFONTINFO* impl = GetImpl(info);
+ return impl->MapFont(impl, weight, italic, charset, pitch_family,
+ renamed_face.c_str(), exact);
+}
+
+void* GetFontImpl(FPDF_SYSFONTINFO* info, const char* face) {
+ // Any non-null return will do.
+ FPDF_SYSFONTINFO* impl = GetImpl(info);
+ std::string renamed_face = TestFonts::RenameFont(face);
+ return impl->GetFont(impl, renamed_face.c_str());
+}
+
+unsigned long GetFontDataImpl(FPDF_SYSFONTINFO* info,
+ void* font,
+ unsigned int table,
+ unsigned char* buffer,
+ unsigned long buf_size) {
+ FPDF_SYSFONTINFO* impl = GetImpl(info);
+ return impl->GetFontData(impl, font, table, buffer, buf_size);
+}
+
+unsigned long GetFaceNameImpl(FPDF_SYSFONTINFO* info,
+ void* font,
+ char* buffer,
+ unsigned long buf_size) {
+ FPDF_SYSFONTINFO* impl = GetImpl(info);
+ return impl->GetFaceName(impl, font, buffer, buf_size);
+}
+
+int GetFontCharsetImpl(FPDF_SYSFONTINFO* info, void* font) {
+ FPDF_SYSFONTINFO* impl = GetImpl(info);
+ return impl->GetFontCharset(impl, font);
+}
+
+void DeleteFontImpl(FPDF_SYSFONTINFO* info, void* font) {
+ FPDF_SYSFONTINFO* impl = GetImpl(info);
+ impl->DeleteFont(impl, font);
+}
+
+} // namespace
+
+FontRenamer::FontRenamer() : impl_(FPDF_GetDefaultSystemFontInfo()) {
+ version = 1;
+ Release = ReleaseImpl;
+ EnumFonts = EnumFontsImpl;
+ MapFont = MapFontImpl;
+ GetFont = GetFontImpl;
+ GetFontData = GetFontDataImpl;
+ GetFaceName = GetFaceNameImpl;
+ GetFontCharset = GetFontCharsetImpl;
+ DeleteFont = DeleteFontImpl;
+ FPDF_SetSystemFontInfo(this);
+}
+
+FontRenamer::~FontRenamer() {
+ FPDF_FreeDefaultSystemFontInfo(impl_.ExtractAsDangling());
+}
diff --git a/testing/font_renamer.h b/testing/font_renamer.h
new file mode 100644
index 0000000..2c32c35
--- /dev/null
+++ b/testing/font_renamer.h
@@ -0,0 +1,22 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TESTING_FONT_RENAMER_H_
+#define TESTING_FONT_RENAMER_H_
+
+#include "core/fxcrt/unowned_ptr.h"
+#include "public/fpdf_sysfontinfo.h"
+
+class FontRenamer final : public FPDF_SYSFONTINFO {
+ public:
+ FontRenamer();
+ ~FontRenamer();
+
+ FPDF_SYSFONTINFO* impl() { return impl_; }
+
+ private:
+ UnownedPtr<FPDF_SYSFONTINFO> impl_;
+};
+
+#endif // TESTING_FONT_RENAMER_H_
diff --git a/testing/free_deleter.h b/testing/free_deleter.h
index 58f40da..d183050 100644
--- a/testing/free_deleter.h
+++ b/testing/free_deleter.h
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
diff --git a/testing/fuzzers/BUILD.gn b/testing/fuzzers/BUILD.gn
index 03e0610..35624cb 100644
--- a/testing/fuzzers/BUILD.gn
+++ b/testing/fuzzers/BUILD.gn
@@ -1,16 +1,16 @@
-# Copyright 2016 The PDFium Authors. All rights reserved.
+# Copyright 2016 The PDFium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+import("//build_overrides/build.gni")
import("../../pdfium.gni")
config("fuzzer_config") {
- configs = [ "../..:pdfium_core_config" ]
-
- defines = [
- "PNG_PREFIX",
- "PNG_USE_READ_MACROS",
+ configs = [
+ "../..:pdfium_strict_config",
+ "../..:pdfium_noshorten_config",
]
+ defines = []
include_dirs = [ "../.." ]
}
@@ -43,9 +43,9 @@
"pdf_cfx_barcode_fuzzer",
"pdf_codec_jpeg_fuzzer",
"pdf_css_fuzzer",
- "pdf_fm2js_fuzzer",
"pdf_formcalc_context_fuzzer",
"pdf_formcalc_fuzzer",
+ "pdf_formcalc_translate_fuzzer",
"pdfium_xfa_fuzzer",
"pdfium_xfa_lpm_fuzzer",
]
@@ -68,45 +68,96 @@
}
if (is_clang) {
# Fuzzers that use FuzzedDataProvider can only be built with Clang.
- fuzzer_list += [ "pdf_nametree_fuzzer" ]
+ fuzzer_list += [
+ "pdf_cpdf_tounicodemap_fuzzer",
+ "pdf_nametree_fuzzer",
+ ]
+ if (pdf_enable_xfa) {
+ fuzzer_list += [
+ "pdf_xfa_fdp_fuzzer",
+ "pdf_xfa_raw_fuzzer",
+ "pdf_xfa_xdp_fdp_fuzzer",
+ ]
+ }
}
+# Note that this only compiles all the fuzzers, to prevent compile breakages.
+# It does not link and create fuzzer executables. That is done in Chromium.
group("fuzzers") {
testonly = true
deps = []
foreach(fuzzer, fuzzer_list) {
deps += [ ":${fuzzer}_src" ]
}
+
+ if (is_component_build) {
+ deps += [ ":fuzzer_impls" ]
+ }
+}
+
+source_set("fuzzer_pdf_templates") {
+ sources = [ "pdf_fuzzer_templates.h" ]
}
source_set("fuzzer_init") {
testonly = true
sources = [ "pdf_fuzzer_init.cc" ]
include_dirs = [ "../.." ]
- deps = [ "../../:pdfium_public_headers" ]
+ deps = [
+ "../../:pdfium_public_headers",
+ "../../fpdfsdk",
+ ]
+}
+
+if (pdf_enable_xfa) {
+ assert(pdf_enable_v8)
+ source_set("fuzzer_xfa_process_state") {
+ testonly = !is_component_build
+ sources = [
+ "xfa_process_state.cc",
+ "xfa_process_state.h",
+ ]
+ configs += [ ":fuzzer_config" ]
+ deps = [
+ "../../fxjs:gc",
+ "//v8",
+ ]
+ }
}
source_set("fuzzer_init_public") {
testonly = true
sources = [ "pdf_fuzzer_init_public.cc" ]
include_dirs = [ "../.." ]
- deps = [ "../../:pdfium_public_headers" ]
+ deps = [
+ ":fuzzer_utils",
+ "../../:pdfium_public_headers",
+ "../../fpdfsdk",
+ ]
if (pdf_enable_v8) {
configs += [ "//v8:external_startup_data" ]
deps += [
"../:test_support",
+ "../../fxjs",
"//v8",
"//v8:v8_libplatform",
]
+ if (pdf_enable_xfa) {
+ deps += [ ":fuzzer_xfa_process_state" ]
+ }
}
}
if (is_component_build) {
group("fuzzer_impls") {
+ testonly = true
deps = []
foreach(fuzzer, fuzzer_list) {
deps += [ ":${fuzzer}_impl" ]
}
+ if (pdf_enable_xfa) {
+ deps += [ ":fuzzer_xfa_process_state" ]
+ }
}
}
@@ -119,6 +170,7 @@
configs += [ ":fuzzer_config" ]
deps = [
"../../:pdfium_public_headers",
+ "../../fpdfsdk",
"../../third_party:pdfium_base",
]
}
@@ -137,18 +189,20 @@
}
template("pdfium_fuzzer") {
- if (defined(invoker.public_fuzzer) && invoker.public_fuzzer) {
+ is_public = defined(invoker.public_fuzzer) && invoker.public_fuzzer
+ if (is_public) {
init_dep = ":fuzzer_init_public"
} else {
init_dep = ":fuzzer_init"
}
if (is_component_build) {
# In component builds, fuzzers are split into "_impl" and "_src" targets.
- # The "_impl" target exports the fuzzer implementation and gets statically
- # linked into the PDFium shared library. The "_src" target is a thin
- # wrapper that imports the fuzzer from PDFium; this gets linked into the
- # real fuzzer executable. In static builds, there's only a single "_src"
- # target that contains the implementation and statically links in PDFium.
+ # The "_impl" target exports the fuzzer implementation. The "_src" target
+ # is a thin wrapper that imports the fuzzer from PDFium; this gets linked
+ # into the real fuzzer executable. The real fuzzer target has to depend on
+ # both the "_impl" and "_src" targets.
+ # In static builds, there's only a single "_src" target that contains the
+ # implementation and statically links in PDFium.
impl_name = target_name + "_impl"
template_target_name = target_name
@@ -156,7 +210,7 @@
testonly = true
sources = [ "component_fuzzer_template.cc" ]
deps = [
- "../../:pdfium",
+ "../../:pdfium_public_headers",
init_dep,
]
configs += [ ":fuzzer_config" ]
@@ -166,16 +220,14 @@
impl_name = target_name + "_src"
}
source_set(impl_name) {
+ testonly = true
sources = invoker.sources
+ defines = []
deps = []
if (defined(invoker.deps)) {
deps += invoker.deps
}
- configs -= [ "//build/config/compiler:chromium_code" ]
- configs += [
- "//build/config/compiler:no_chromium_code",
- ":fuzzer_config",
- ]
+ configs += [ ":fuzzer_config" ]
if (is_component_build) {
# |export| should be consistent with FPDF_EXPORT In public/fpdfview.h.
if (is_win) {
@@ -183,7 +235,7 @@
} else {
export = "__attribute__((visibility(\"default\")))"
}
- defines = [ "LLVMFuzzerTestOneInput=${export} ${template_target_name}" ]
+ defines += [ "LLVMFuzzerTestOneInput=${export} ${template_target_name}" ]
deps += [ "../../:pdfium_public_headers" ]
} else {
testonly = true
@@ -192,6 +244,12 @@
init_dep,
]
}
+ if (is_public && pdf_enable_xfa) {
+ deps += [ ":fuzzer_xfa_process_state" ]
+ }
+ if (build_with_chromium) {
+ defines += [ "BUILD_WITH_CHROMIUM" ]
+ }
}
}
@@ -200,6 +258,7 @@
sources = [ "pdf_cjs_util_fuzzer.cc" ]
deps = [
"../../core/fxcrt",
+ "../../fpdfsdk",
"../../fxjs",
]
}
@@ -207,6 +266,7 @@
sources = [ "pdf_fx_date_helpers_fuzzer.cc" ]
deps = [
"../../core/fxcrt",
+ "../../fpdfsdk",
"../../fxjs",
]
}
@@ -218,7 +278,7 @@
"../../:freetype_common",
"../../core/fxcrt",
"../../core/fxge",
- "../../xfa/fgas",
+ "../../xfa/fgas/font",
"../../xfa/fgas/layout",
"//third_party/icu:icuuc",
]
@@ -227,10 +287,15 @@
pdfium_fuzzer("pdf_cfgas_stringformatter_fuzzer") {
sources = [ "pdf_cfgas_stringformatter_fuzzer.cc" ]
deps = [
+ ":fuzzer_utils",
"../../core/fxcrt",
- "../../xfa/fgas",
+ "../../fpdfsdk",
+ "../../fxjs:gc",
+ "../../xfa/fgas/crt",
+ "../../xfa/fxfa",
"../../xfa/fxfa/parser",
]
+ public_fuzzer = true
}
pdfium_fuzzer("pdf_cfx_barcode_fuzzer") {
@@ -331,12 +396,15 @@
]
}
- pdfium_fuzzer("pdf_fm2js_fuzzer") {
- sources = [ "pdf_fm2js_fuzzer.cc" ]
+ pdfium_fuzzer("pdf_formcalc_translate_fuzzer") {
+ sources = [ "pdf_formcalc_translate_fuzzer.cc" ]
deps = [
+ ":fuzzer_utils",
"../../core/fxcrt",
+ "../../fpdfsdk",
"../../fxjs",
]
+ public_fuzzer = true
}
pdfium_fuzzer("pdf_formcalc_context_fuzzer") {
@@ -356,9 +424,12 @@
pdfium_fuzzer("pdf_formcalc_fuzzer") {
sources = [ "pdf_formcalc_fuzzer.cc" ]
deps = [
+ ":fuzzer_utils",
"../../core/fxcrt",
- "../../xfa/fxfa/fm2js",
+ "../../fxjs:gc",
+ "../../xfa/fxfa/formcalc",
]
+ public_fuzzer = true
}
pdfium_fuzzer("pdfium_xfa_fuzzer") {
@@ -385,6 +456,15 @@
}
if (is_clang) {
+ pdfium_fuzzer("pdf_cpdf_tounicodemap_fuzzer") {
+ sources = [ "pdf_cpdf_tounicodemap_fuzzer.cc" ]
+ deps = [
+ "../../core/fpdfapi/font",
+ "../../core/fpdfapi/parser",
+ "../../core/fxcrt",
+ ]
+ }
+
pdfium_fuzzer("pdf_nametree_fuzzer") {
sources = [ "pdf_nametree_fuzzer.cc" ]
deps = [
@@ -394,6 +474,34 @@
"../../third_party:pdfium_base",
]
}
+ if (pdf_enable_xfa) {
+ pdfium_fuzzer("pdf_xfa_fdp_fuzzer") {
+ sources = [ "pdf_xfa_fdp_fuzzer.cc" ]
+ deps = [
+ ":fuzzer_helper",
+ ":fuzzer_pdf_templates",
+ "../../third_party:pdfium_base",
+ ]
+ public_fuzzer = true
+ }
+ pdfium_fuzzer("pdf_xfa_raw_fuzzer") {
+ sources = [ "pdf_xfa_raw_fuzzer.cc" ]
+ deps = [
+ ":fuzzer_helper",
+ ":fuzzer_pdf_templates",
+ "../../third_party:pdfium_base",
+ ]
+ public_fuzzer = true
+ }
+ pdfium_fuzzer("pdf_xfa_xdp_fdp_fuzzer") {
+ sources = [ "pdf_xfa_xdp_fdp_fuzzer.cc" ]
+ deps = [
+ ":fuzzer_helper",
+ ":fuzzer_pdf_templates",
+ ]
+ public_fuzzer = true
+ }
+ }
}
pdfium_fuzzer("pdf_cmap_fuzzer") {
@@ -401,6 +509,7 @@
deps = [
"../../:freetype_common",
"../../core/fpdfapi/font",
+ "../../core/fxcrt",
"../../third_party:pdfium_base",
]
}
@@ -485,6 +594,7 @@
sources = [ "pdf_scanlinecompositor_fuzzer.cc" ]
deps = [
":fuzzer_utils",
+ "../../core/fxcrt",
"../../core/fxge",
"../../third_party:pdfium_base",
]
@@ -510,5 +620,11 @@
pdfium_fuzzer("pdfium_fuzzer") {
sources = [ "pdfium_fuzzer.cc" ]
deps = [ ":fuzzer_helper" ]
+ if (build_with_chromium) {
+ deps += [
+ "//base",
+ "//base/test:test_support",
+ ]
+ }
public_fuzzer = true
}
diff --git a/testing/fuzzers/DEPS b/testing/fuzzers/DEPS
index fe9eaf6..d577044 100644
--- a/testing/fuzzers/DEPS
+++ b/testing/fuzzers/DEPS
@@ -1,4 +1,7 @@
include_rules = [
'+fxbarcode',
'+xfa',
+
+ # Only used when the fuzzer is embedded in Chromium.
+ '+base',
]
diff --git a/testing/fuzzers/component_fuzzer_template.cc b/testing/fuzzers/component_fuzzer_template.cc
index 89883f5..6f65bc1 100644
--- a/testing/fuzzers/component_fuzzer_template.cc
+++ b/testing/fuzzers/component_fuzzer_template.cc
@@ -1,9 +1,9 @@
-// Copyright 2019 The PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include <cstddef>
-#include <cstdint>
+#include <stddef.h>
+#include <stdint.h>
#include "public/fpdfview.h"
diff --git a/testing/fuzzers/pdf_bidi_fuzzer.cc b/testing/fuzzers/pdf_bidi_fuzzer.cc
index 614df52..c0ca776 100644
--- a/testing/fuzzers/pdf_bidi_fuzzer.cc
+++ b/testing/fuzzers/pdf_bidi_fuzzer.cc
@@ -1,28 +1,30 @@
-// Copyright 2018 The PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
// 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/fxcrt/fx_codepage.h"
#include "core/fxcrt/widestring.h"
#include "core/fxge/cfx_font.h"
#include "core/fxge/fx_font.h"
-#include "third_party/base/ptr_util.h"
-#include "xfa/fgas/font/cfgas_fontmgr.h"
#include "xfa/fgas/font/cfgas_gefont.h"
-#include "xfa/fgas/layout/cfx_char.h"
-#include "xfa/fgas/layout/cfx_rtfbreak.h"
+#include "xfa/fgas/layout/cfgas_char.h"
+#include "xfa/fgas/layout/cfgas_rtfbreak.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- auto fontmgr = pdfium::MakeUnique<CFGAS_FontMgr>();
+ if (size > 8192)
+ return 0;
- auto font = pdfium::MakeUnique<CFX_Font>();
- font->LoadSubst("Arial", true, 0, FXFONT_FW_NORMAL, 0, 0, 0);
+ auto font = std::make_unique<CFX_Font>();
+ font->LoadSubst("Arial", true, 0, FXFONT_FW_NORMAL, 0, FX_CodePage::kDefANSI,
+ 0);
assert(font);
- CFX_RTFBreak rtf_break(FX_LAYOUTSTYLE_ExpandTab);
+ CFGAS_RTFBreak rtf_break(CFGAS_Break::LayoutStyle::kExpandTab);
rtf_break.SetLineBreakTolerance(1);
- rtf_break.SetFont(CFGAS_GEFont::LoadFont(std::move(font), fontmgr.get()));
+ rtf_break.SetFont(CFGAS_GEFont::LoadFont(std::move(font)));
rtf_break.SetFontSize(12);
WideString input =
@@ -31,8 +33,8 @@
for (wchar_t ch : input)
rtf_break.AppendChar(ch);
- std::vector<CFX_Char> chars =
+ std::vector<CFGAS_Char> chars =
rtf_break.GetCurrentLineForTesting()->m_LineChars;
- CFX_Char::BidiLine(&chars, chars.size());
+ CFGAS_Char::BidiLine(&chars, chars.size());
return 0;
}
diff --git a/testing/fuzzers/pdf_cfgas_stringformatter_fuzzer.cc b/testing/fuzzers/pdf_cfgas_stringformatter_fuzzer.cc
index b6a6663..12f6206 100644
--- a/testing/fuzzers/pdf_cfgas_stringformatter_fuzzer.cc
+++ b/testing/fuzzers/pdf_cfgas_stringformatter_fuzzer.cc
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -6,17 +6,25 @@
#include <stdint.h>
-#include "core/fxcrt/fx_memory.h"
+#include <iterator>
+
+#include "core/fxcrt/cfx_datetime.h"
#include "core/fxcrt/fx_string.h"
-#include "third_party/base/ptr_util.h"
+#include "public/fpdfview.h"
+#include "testing/fuzzers/pdfium_fuzzer_util.h"
+#include "testing/fuzzers/xfa_process_state.h"
+#include "v8/include/cppgc/heap.h"
+#include "v8/include/cppgc/persistent.h"
#include "xfa/fxfa/parser/cxfa_localemgr.h"
namespace {
const wchar_t* const kLocales[] = {L"en", L"fr", L"jp", L"zh"};
-const FX_DATETIMETYPE kTypes[] = {FX_DATETIMETYPE_Date, FX_DATETIMETYPE_Time,
- FX_DATETIMETYPE_DateTime,
- FX_DATETIMETYPE_TimeDate};
+const CFGAS_StringFormatter::DateTimeType kTypes[] = {
+ CFGAS_StringFormatter::DateTimeType::kDate,
+ CFGAS_StringFormatter::DateTimeType::kTime,
+ CFGAS_StringFormatter::DateTimeType::kDateTime,
+ CFGAS_StringFormatter::DateTimeType::kTimeDate};
} // namespace
@@ -24,16 +32,12 @@
if (size < 5 || size > 128) // Big strings are unlikely to help.
return 0;
- // Static for speed.
- static std::vector<std::unique_ptr<CXFA_LocaleMgr>> mgrs;
- if (mgrs.empty()) {
- for (const auto* locale : kLocales)
- mgrs.push_back(pdfium::MakeUnique<CXFA_LocaleMgr>(nullptr, locale));
- }
+ auto* state = static_cast<XFAProcessState*>(FPDF_GetFuzzerPerProcessState());
+ cppgc::Heap* heap = state->GetHeap();
uint8_t test_selector = data[0] % 10;
- uint8_t locale_selector = data[1] % FX_ArraySize(kLocales);
- uint8_t type_selector = data[2] % FX_ArraySize(kTypes);
+ uint8_t locale_selector = data[1] % std::size(kLocales);
+ uint8_t type_selector = data[2] % std::size(kTypes);
data += 3;
size -= 3;
@@ -44,8 +48,7 @@
WideString value =
WideString::FromLatin1(ByteStringView(data + pattern_len, value_len));
- auto fmt = pdfium::MakeUnique<CFGAS_StringFormatter>(
- mgrs[locale_selector].get(), pattern);
+ auto fmt = std::make_unique<CFGAS_StringFormatter>(pattern);
WideString result;
CFX_DateTime dt;
@@ -53,33 +56,55 @@
case 0:
fmt->FormatText(value, &result);
break;
- case 1:
- fmt->FormatNum(value, &result);
+ case 1: {
+ auto* mgr = cppgc::MakeGarbageCollected<CXFA_LocaleMgr>(
+ heap->GetAllocationHandle(), heap, nullptr,
+ kLocales[locale_selector]);
+ fmt->FormatNum(mgr, value, &result);
break;
- case 2:
- fmt->FormatDateTime(value, kTypes[type_selector], &result);
+ }
+ case 2: {
+ auto* mgr = cppgc::MakeGarbageCollected<CXFA_LocaleMgr>(
+ heap->GetAllocationHandle(), heap, nullptr,
+ kLocales[locale_selector]);
+ fmt->FormatDateTime(mgr, value, kTypes[type_selector], &result);
break;
- case 3:
+ }
+ case 3: {
fmt->FormatNull(&result);
break;
- case 4:
+ }
+ case 4: {
fmt->FormatZero(&result);
break;
- case 5:
+ }
+ case 5: {
fmt->ParseText(value, &result);
break;
- case 6:
- fmt->ParseNum(value, &result);
+ }
+ case 6: {
+ auto* mgr = cppgc::MakeGarbageCollected<CXFA_LocaleMgr>(
+ heap->GetAllocationHandle(), heap, nullptr,
+ kLocales[locale_selector]);
+ fmt->ParseNum(mgr, value, &result);
break;
- case 7:
- fmt->ParseDateTime(value, kTypes[type_selector], &dt);
+ }
+ case 7: {
+ auto* mgr = cppgc::MakeGarbageCollected<CXFA_LocaleMgr>(
+ heap->GetAllocationHandle(), heap, nullptr,
+ kLocales[locale_selector]);
+ fmt->ParseDateTime(mgr, value, kTypes[type_selector], &dt);
break;
- case 8:
+ }
+ case 8: {
fmt->ParseNull(value);
break;
- case 9:
+ }
+ case 9: {
fmt->ParseZero(value);
break;
+ }
}
+ state->ForceGCAndPump();
return 0;
}
diff --git a/testing/fuzzers/pdf_cfx_barcode_fuzzer.cc b/testing/fuzzers/pdf_cfx_barcode_fuzzer.cc
index 7b31b2b..35afec9 100644
--- a/testing/fuzzers/pdf_cfx_barcode_fuzzer.cc
+++ b/testing/fuzzers/pdf_cfx_barcode_fuzzer.cc
@@ -1,9 +1,7 @@
-// Copyright 2017 The PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include <memory>
-
#include "core/fxcrt/fx_string.h"
#include "fxbarcode/cfx_barcode.h"
@@ -11,7 +9,8 @@
if (size < 2 * sizeof(uint16_t))
return 0;
- BC_TYPE type = static_cast<BC_TYPE>(data[0] % (BC_LAST + 1));
+ BC_TYPE type =
+ static_cast<BC_TYPE>(data[0] % (static_cast<int>(BC_TYPE::kLast) + 1));
// Only used one byte, but align with uint16_t for string below.
data += sizeof(uint16_t);
diff --git a/testing/fuzzers/pdf_cjs_util_fuzzer.cc b/testing/fuzzers/pdf_cjs_util_fuzzer.cc
index 5ccb65b..3885c7b 100644
--- a/testing/fuzzers/pdf_cjs_util_fuzzer.cc
+++ b/testing/fuzzers/pdf_cjs_util_fuzzer.cc
@@ -1,4 +1,4 @@
-// Copyright 2018 The PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
diff --git a/testing/fuzzers/pdf_cmap_fuzzer.cc b/testing/fuzzers/pdf_cmap_fuzzer.cc
index 180a6a7..d4f9c70 100644
--- a/testing/fuzzers/pdf_cmap_fuzzer.cc
+++ b/testing/fuzzers/pdf_cmap_fuzzer.cc
@@ -1,10 +1,11 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include <cstdint>
+#include <stdint.h>
#include "core/fpdfapi/font/cpdf_cmap.h"
+#include "core/fxcrt/retain_ptr.h"
#include "third_party/base/span.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
diff --git a/testing/fuzzers/pdf_codec_a85_fuzzer.cc b/testing/fuzzers/pdf_codec_a85_fuzzer.cc
index f9ae1fe..2b1614d 100644
--- a/testing/fuzzers/pdf_codec_a85_fuzzer.cc
+++ b/testing/fuzzers/pdf_codec_a85_fuzzer.cc
@@ -1,16 +1,12 @@
-// Copyright 2017 The PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
// 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/basic/basicmodule.h"
-#include "core/fxcrt/fx_memory_wrappers.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- std::unique_ptr<uint8_t, FxFreeDeleter> dest_buf;
- uint32_t dest_size = 0;
- BasicModule::A85Encode({data, size}, &dest_buf, &dest_size);
+ BasicModule::A85Encode({data, size});
return 0;
}
diff --git a/testing/fuzzers/pdf_codec_bmp_fuzzer.cc b/testing/fuzzers/pdf_codec_bmp_fuzzer.cc
index 71f9150..1bc023a 100644
--- a/testing/fuzzers/pdf_codec_bmp_fuzzer.cc
+++ b/testing/fuzzers/pdf_codec_bmp_fuzzer.cc
@@ -1,4 +1,4 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
diff --git a/testing/fuzzers/pdf_codec_fax_fuzzer.cc b/testing/fuzzers/pdf_codec_fax_fuzzer.cc
index d0c2984..793d18e 100644
--- a/testing/fuzzers/pdf_codec_fax_fuzzer.cc
+++ b/testing/fuzzers/pdf_codec_fax_fuzzer.cc
@@ -1,4 +1,4 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -38,7 +38,7 @@
if (decoder) {
int line = 0;
- while (decoder->GetScanline(line))
+ while (!decoder->GetScanline(line).empty())
line++;
}
diff --git a/testing/fuzzers/pdf_codec_gif_fuzzer.cc b/testing/fuzzers/pdf_codec_gif_fuzzer.cc
index 69129e7..4adbad7 100644
--- a/testing/fuzzers/pdf_codec_gif_fuzzer.cc
+++ b/testing/fuzzers/pdf_codec_gif_fuzzer.cc
@@ -1,4 +1,4 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
diff --git a/testing/fuzzers/pdf_codec_icc_fuzzer.cc b/testing/fuzzers/pdf_codec_icc_fuzzer.cc
index ca027331..4db79d2 100644
--- a/testing/fuzzers/pdf_codec_icc_fuzzer.cc
+++ b/testing/fuzzers/pdf_codec_icc_fuzzer.cc
@@ -1,23 +1,21 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <cstdint>
-#include "core/fxcodec/icc/iccmodule.h"
+#include "core/fxcodec/icc/icc_transform.h"
#include "third_party/base/span.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- std::unique_ptr<CLcmsCmm> transform =
- IccModule::CreateTransformSRGB(pdfium::make_span(data, size));
+ std::unique_ptr<fxcodec::IccTransform> transform =
+ fxcodec::IccTransform::CreateTransformSRGB(pdfium::make_span(data, size));
+ if (!transform)
+ return 0;
- if (transform) {
- float src[4];
- float dst[4];
- for (int i = 0; i < 4; i++)
- src[i] = 0.5f;
- IccModule::Translate(transform.get(), transform->components(), src, dst);
- }
-
+ const float src[4] = {0.5f, 0.5f, 0.5f, 0.5f};
+ float dst[4];
+ transform->Translate(pdfium::make_span(src).first(transform->components()),
+ pdfium::make_span(dst));
return 0;
}
diff --git a/testing/fuzzers/pdf_codec_jbig2_fuzzer.cc b/testing/fuzzers/pdf_codec_jbig2_fuzzer.cc
index 2878d7c..59eda76 100644
--- a/testing/fuzzers/pdf_codec_jbig2_fuzzer.cc
+++ b/testing/fuzzers/pdf_codec_jbig2_fuzzer.cc
@@ -1,17 +1,16 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include <cstdint>
+#include <stdint.h>
#include "core/fxcodec/jbig2/JBig2_Context.h"
#include "core/fxcodec/jbig2/JBig2_DocumentContext.h"
-#include "core/fxcodec/jbig2/jbig2module.h"
+#include "core/fxcodec/jbig2/jbig2_decoder.h"
#include "core/fxcrt/fx_safe_types.h"
#include "core/fxge/dib/cfx_dibitmap.h"
-#include "core/fxge/fx_dib.h"
+#include "core/fxge/dib/fx_dib.h"
#include "testing/fuzzers/pdfium_fuzzer_util.h"
-#include "third_party/base/ptr_util.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
const size_t kParameterSize = 8;
@@ -32,17 +31,16 @@
return 0;
auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
- if (!bitmap->Create(width, height, FXDIB_1bppRgb))
+ if (!bitmap->Create(width, height, FXDIB_Format::k1bppRgb))
return 0;
- Jbig2Module module;
+ JBig2_DocumentContext document_context;
Jbig2Context jbig2_context;
- std::unique_ptr<JBig2_DocumentContext> document_context;
- FXCODEC_STATUS status = module.StartDecode(
+ FXCODEC_STATUS status = Jbig2Decoder::StartDecode(
&jbig2_context, &document_context, width, height, {data, size}, 1, {}, 0,
bitmap->GetBuffer(), bitmap->GetPitch(), nullptr);
- while (status == FXCODEC_STATUS_DECODE_TOBECONTINUE)
- status = module.ContinueDecode(&jbig2_context, nullptr);
+ while (status == FXCODEC_STATUS::kDecodeToBeContinued)
+ status = Jbig2Decoder::ContinueDecode(&jbig2_context, nullptr);
return 0;
}
diff --git a/testing/fuzzers/pdf_codec_jpeg_fuzzer.cc b/testing/fuzzers/pdf_codec_jpeg_fuzzer.cc
index eaa0889..a9f7216 100644
--- a/testing/fuzzers/pdf_codec_jpeg_fuzzer.cc
+++ b/testing/fuzzers/pdf_codec_jpeg_fuzzer.cc
@@ -1,4 +1,4 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
diff --git a/testing/fuzzers/pdf_codec_png_fuzzer.cc b/testing/fuzzers/pdf_codec_png_fuzzer.cc
index 61a6574..14d9bd3 100644
--- a/testing/fuzzers/pdf_codec_png_fuzzer.cc
+++ b/testing/fuzzers/pdf_codec_png_fuzzer.cc
@@ -1,4 +1,4 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
diff --git a/testing/fuzzers/pdf_codec_rle_fuzzer.cc b/testing/fuzzers/pdf_codec_rle_fuzzer.cc
index 7b40b01..70ce5d4 100644
--- a/testing/fuzzers/pdf_codec_rle_fuzzer.cc
+++ b/testing/fuzzers/pdf_codec_rle_fuzzer.cc
@@ -1,16 +1,12 @@
-// Copyright 2017 The PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
// 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/basic/basicmodule.h"
-#include "core/fxcrt/fx_memory_wrappers.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- std::unique_ptr<uint8_t, FxFreeDeleter> dest_buf;
- uint32_t dest_size = 0;
- BasicModule::RunLengthEncode({data, size}, &dest_buf, &dest_size);
+ BasicModule::RunLengthEncode({data, size});
return 0;
}
diff --git a/testing/fuzzers/pdf_codec_tiff_fuzzer.cc b/testing/fuzzers/pdf_codec_tiff_fuzzer.cc
index 187c311..6566b6b 100644
--- a/testing/fuzzers/pdf_codec_tiff_fuzzer.cc
+++ b/testing/fuzzers/pdf_codec_tiff_fuzzer.cc
@@ -1,4 +1,4 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
diff --git a/testing/fuzzers/pdf_cpdf_tounicodemap_fuzzer.cc b/testing/fuzzers/pdf_cpdf_tounicodemap_fuzzer.cc
new file mode 100644
index 0000000..5f18564
--- /dev/null
+++ b/testing/fuzzers/pdf_cpdf_tounicodemap_fuzzer.cc
@@ -0,0 +1,38 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "core/fpdfapi/font/cpdf_tounicodemap.h"
+#include "core/fpdfapi/parser/cpdf_stream.h"
+#include "core/fxcrt/retain_ptr.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ static constexpr size_t kParameterSize = sizeof(uint32_t) + sizeof(wchar_t);
+ if (size <= kParameterSize)
+ return 0;
+
+ // Limit data size to prevent fuzzer timeout.
+ static constexpr size_t kMaxDataSize = 256 * 1024;
+ if (size > kParameterSize + kMaxDataSize)
+ return 0;
+
+ FuzzedDataProvider data_provider(data, size);
+ uint32_t charcode_to_lookup = data_provider.ConsumeIntegral<uint32_t>();
+ wchar_t char_for_reverse_lookup = data_provider.ConsumeIntegral<wchar_t>();
+
+ std::vector<uint8_t> remaining =
+ data_provider.ConsumeRemainingBytes<uint8_t>();
+ auto stream = pdfium::MakeRetain<CPDF_Stream>();
+ stream->SetData(remaining);
+
+ auto to_unicode_map = std::make_unique<CPDF_ToUnicodeMap>(std::move(stream));
+ to_unicode_map->Lookup(charcode_to_lookup);
+ to_unicode_map->ReverseLookup(char_for_reverse_lookup);
+ return 0;
+}
diff --git a/testing/fuzzers/pdf_css_fuzzer.cc b/testing/fuzzers/pdf_css_fuzzer.cc
index 5f1471d..f11705c 100644
--- a/testing/fuzzers/pdf_css_fuzzer.cc
+++ b/testing/fuzzers/pdf_css_fuzzer.cc
@@ -1,9 +1,7 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include <memory>
-
#include "core/fxcrt/css/cfx_css.h"
#include "core/fxcrt/css/cfx_csssyntaxparser.h"
#include "core/fxcrt/fx_string.h"
@@ -16,11 +14,11 @@
if (input.IsEmpty())
return 0;
- CFX_CSSSyntaxParser parser(input.c_str(), input.GetLength());
- CFX_CSSSyntaxStatus status;
+ CFX_CSSSyntaxParser parser(input.AsStringView());
+ CFX_CSSSyntaxParser::Status status;
do {
status = parser.DoSyntaxParse();
- } while (status != CFX_CSSSyntaxStatus::Error &&
- status != CFX_CSSSyntaxStatus::EOS);
+ } while (status != CFX_CSSSyntaxParser::Status::kError &&
+ status != CFX_CSSSyntaxParser::Status::kEOS);
return 0;
}
diff --git a/testing/fuzzers/pdf_fm2js_fuzzer.cc b/testing/fuzzers/pdf_fm2js_fuzzer.cc
deleted file mode 100644
index 67daa46..0000000
--- a/testing/fuzzers/pdf_fm2js_fuzzer.cc
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2016 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 <cstddef>
-#include <cstdint>
-
-#include "core/fxcrt/cfx_widetextbuf.h"
-#include "core/fxcrt/fx_safe_types.h"
-#include "core/fxcrt/fx_string.h"
-#include "fxjs/xfa/cfxjse_formcalc_context.h"
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- FX_SAFE_SIZE_T safe_size = size;
- if (!safe_size.IsValid())
- return 0;
-
- CFX_WideTextBuf js;
- WideString input =
- WideString::FromUTF8(ByteStringView(data, safe_size.ValueOrDie()));
- CFXJSE_FormCalcContext::Translate(input.AsStringView(), &js);
- return 0;
-}
diff --git a/testing/fuzzers/pdf_font_fuzzer.cc b/testing/fuzzers/pdf_font_fuzzer.cc
index 7c59630..a02052a 100644
--- a/testing/fuzzers/pdf_font_fuzzer.cc
+++ b/testing/fuzzers/pdf_font_fuzzer.cc
@@ -1,16 +1,15 @@
-// Copyright 2017 The PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include <cstring>
-#include <memory>
-
#include "public/cpp/fpdf_scopers.h"
#include "public/fpdf_edit.h"
#include "public/fpdfview.h"
+static constexpr size_t kMaxFuzzBytes = 1024 * 1024 * 1024; // 1 GB.
+
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- if (size < 2)
+ if (size < 2 || size > kMaxFuzzBytes)
return 0;
ScopedFPDFDocument doc(FPDF_CreateNewDocument());
@@ -19,7 +18,8 @@
FPDF_BOOL cid = data[1];
data += 2;
size -= 2;
- ScopedFPDFFont font(FPDFText_LoadFont(doc.get(), data, size, font_type, cid));
+ ScopedFPDFFont font(FPDFText_LoadFont(
+ doc.get(), data, static_cast<uint32_t>(size), font_type, cid));
if (!font)
return 0;
diff --git a/testing/fuzzers/pdf_formcalc_context_fuzzer.cc b/testing/fuzzers/pdf_formcalc_context_fuzzer.cc
index e2d73a8..638d086 100644
--- a/testing/fuzzers/pdf_formcalc_context_fuzzer.cc
+++ b/testing/fuzzers/pdf_formcalc_context_fuzzer.cc
@@ -1,9 +1,11 @@
-// Copyright 2019 The PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stdint.h>
+#include <memory>
+
#include "core/fxcrt/fx_string.h"
#include "fpdfsdk/cpdfsdk_helpers.h"
#include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
@@ -11,7 +13,6 @@
#include "fxjs/xfa/cfxjse_value.h"
#include "public/fpdf_formfill.h"
#include "testing/fuzzers/pdfium_fuzzer_helper.h"
-#include "v8/include/v8.h"
#include "xfa/fxfa/cxfa_eventparam.h"
namespace {
@@ -522,15 +523,14 @@
CXFA_EventParam params;
params.m_bCancelAction = false;
- script_context->SetEventParam(¶ms);
+ CFXJSE_Engine::EventParamScope param_scope(script_context, nullptr,
+ ¶ms);
ByteStringView data_view(data_, size_);
- auto value = pdfium::MakeUnique<CFXJSE_Value>(script_context->GetIsolate());
+ auto value = std::make_unique<CFXJSE_Value>();
script_context->RunScript(CXFA_Script::Type::Formcalc,
WideString::FromUTF8(data_view).AsStringView(),
value.get(), xfa_document->GetRoot());
-
- script_context->SetEventParam(nullptr);
}
private:
diff --git a/testing/fuzzers/pdf_formcalc_fuzzer.cc b/testing/fuzzers/pdf_formcalc_fuzzer.cc
index 08e22bb..d8a2a89 100644
--- a/testing/fuzzers/pdf_formcalc_fuzzer.cc
+++ b/testing/fuzzers/pdf_formcalc_fuzzer.cc
@@ -1,16 +1,18 @@
-// Copyright 2017 The PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "core/fxcrt/cfx_widetextbuf.h"
#include "core/fxcrt/fx_string.h"
-#include "xfa/fxfa/fm2js/cxfa_fmparser.h"
+#include "testing/fuzzers/pdfium_fuzzer_util.h"
+#include "testing/fuzzers/xfa_process_state.h"
+#include "xfa/fxfa/formcalc/cxfa_fmparser.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ auto* state = static_cast<XFAProcessState*>(FPDF_GetFuzzerPerProcessState());
WideString input = WideString::FromUTF8(ByteStringView(data, size));
-
- CXFA_FMParser parser(input.AsStringView());
+ CXFA_FMLexer lexer(input.AsStringView());
+ CXFA_FMParser parser(state->GetHeap(), &lexer);
parser.Parse();
-
+ state->ForceGCAndPump();
return 0;
}
diff --git a/testing/fuzzers/pdf_formcalc_translate_fuzzer.cc b/testing/fuzzers/pdf_formcalc_translate_fuzzer.cc
new file mode 100644
index 0000000..82b8188
--- /dev/null
+++ b/testing/fuzzers/pdf_formcalc_translate_fuzzer.cc
@@ -0,0 +1,20 @@
+// Copyright 2016 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/fx_string.h"
+#include "fxjs/xfa/cfxjse_formcalc_context.h"
+#include "testing/fuzzers/pdfium_fuzzer_util.h"
+#include "testing/fuzzers/xfa_process_state.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ auto* state = static_cast<XFAProcessState*>(FPDF_GetFuzzerPerProcessState());
+ WideString input = WideString::FromUTF8(ByteStringView(data, size));
+ CFXJSE_FormCalcContext::Translate(state->GetHeap(), input.AsStringView());
+ state->ForceGCAndPump();
+ return 0;
+}
diff --git a/testing/fuzzers/pdf_fuzzer_init.cc b/testing/fuzzers/pdf_fuzzer_init.cc
index 954eed0..9a75dd2 100644
--- a/testing/fuzzers/pdf_fuzzer_init.cc
+++ b/testing/fuzzers/pdf_fuzzer_init.cc
@@ -1,4 +1,4 @@
-// Copyright 2017 The PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
diff --git a/testing/fuzzers/pdf_fuzzer_init_public.cc b/testing/fuzzers/pdf_fuzzer_init_public.cc
index 5ece0bc..3227d47 100644
--- a/testing/fuzzers/pdf_fuzzer_init_public.cc
+++ b/testing/fuzzers/pdf_fuzzer_init_public.cc
@@ -1,24 +1,33 @@
-// Copyright 2019 The PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include <string.h>
+#include "testing/fuzzers/pdf_fuzzer_init_public.h"
-#include <memory>
+#include <string.h> // For memset()
-#include "public/fpdf_ext.h"
+#include <string>
+
+#include "testing/fuzzers/pdfium_fuzzer_util.h"
#ifdef PDF_ENABLE_V8
#include "testing/free_deleter.h"
#include "testing/v8_initializer.h"
#include "v8/include/v8-platform.h"
-#include "v8/include/v8.h"
-#endif
+#ifdef PDF_ENABLE_XFA
+#include "testing/fuzzers/xfa_process_state.h"
+#include "v8/include/v8-array-buffer.h"
+#include "v8/include/v8-isolate.h"
+#endif // PDF_ENABLE_XFA
+#endif // PDF_ENABLE_V8
#ifdef _WIN32
#include <windows.h>
#elif defined(__APPLE__)
#include <mach-o/dyld.h>
+#elif defined(__Fuchsia__)
+#include <limits.h>
+#include <unistd.h>
#else // Linux
#include <linux/limits.h>
#include <unistd.h>
@@ -26,10 +35,13 @@
namespace {
+// pdf_fuzzer_init.cc and pdf_fuzzer_init_public.cc are mutually exclusive
+// and should not be built together.
+PDFFuzzerInitPublic* g_instance = new PDFFuzzerInitPublic();
+
#ifdef PDF_ENABLE_V8
std::string ProgramPath() {
std::string result;
-
#ifdef _WIN32
char path[MAX_PATH];
DWORD len = GetModuleFileNameA(nullptr, path, MAX_PATH);
@@ -56,41 +68,45 @@
} // namespace
-// Initialize the library once for all runs of the fuzzer.
-struct TestCase {
- TestCase() {
+PDFFuzzerInitPublic::PDFFuzzerInitPublic() {
#ifdef PDF_ENABLE_V8
#ifdef V8_USE_EXTERNAL_STARTUP_DATA
- platform = InitializeV8ForPDFiumWithStartupData(
- ProgramPath(), std::string(), &snapshot_blob);
-#else
- platform = InitializeV8ForPDFium(ProgramPath());
+ platform_ = InitializeV8ForPDFiumWithStartupData(
+ ProgramPath(), std::string(), std::string(), &snapshot_blob_);
+#else // V8_USE_EXTERNAL_STARTUP_DATA
+ platform_ = InitializeV8ForPDFium(ProgramPath(), std::string());
#endif // V8_USE_EXTERNAL_STARTUP_DATA
+#ifdef PDF_ENABLE_XFA
+ allocator_.reset(v8::ArrayBuffer::Allocator::NewDefaultAllocator());
+ v8::Isolate::CreateParams create_params;
+ create_params.array_buffer_allocator = allocator_.get();
+ isolate_.reset(v8::Isolate::New(create_params));
+#endif // PDF_ENABLE_XFA
#endif // PDF_ENABLE_V8
-
- memset(&config, '\0', sizeof(config));
- config.version = 2;
- config.m_pUserFontPaths = nullptr;
- config.m_pIsolate = nullptr;
- config.m_v8EmbedderSlot = 0;
- FPDF_InitLibraryWithConfig(&config);
-
- memset(&unsupport_info, '\0', sizeof(unsupport_info));
- unsupport_info.version = 1;
- unsupport_info.FSDK_UnSupport_Handler = [](UNSUPPORT_INFO*, int) {};
- FSDK_SetUnSpObjProcessHandler(&unsupport_info);
- }
-
+ memset(&config_, '\0', sizeof(config_));
+ config_.version = 3;
+ config_.m_pUserFontPaths = nullptr;
+ config_.m_pPlatform = nullptr;
+ config_.m_pIsolate = nullptr;
+ config_.m_v8EmbedderSlot = 0;
#ifdef PDF_ENABLE_V8
- std::unique_ptr<v8::Platform> platform;
- v8::StartupData snapshot_blob;
+ config_.m_pPlatform = platform_.get();
+ config_.m_pIsolate = isolate_.get();
+#endif // PDF_ENABLE_V8
+ FPDF_InitLibraryWithConfig(&config_);
+
+ memset(&unsupport_info_, '\0', sizeof(unsupport_info_));
+ unsupport_info_.version = 1;
+ unsupport_info_.FSDK_UnSupport_Handler = [](UNSUPPORT_INFO*, int) {};
+ FSDK_SetUnSpObjProcessHandler(&unsupport_info_);
+
+#ifdef PDF_ENABLE_XFA
+ xfa_process_state_ =
+ std::make_unique<XFAProcessState>(platform_.get(), isolate_.get());
+ FPDF_SetFuzzerPerProcessState(xfa_process_state_.get());
#endif
+}
- FPDF_LIBRARY_CONFIG config;
- UNSUPPORT_INFO unsupport_info;
-};
-
-// pdf_fuzzer_init.cc and pdfium_fuzzer_init_public.cc are mutually exclusive
-// and should not be built together. They deliberately have the same global
-// variable.
-static TestCase* g_test_case = new TestCase();
+PDFFuzzerInitPublic::~PDFFuzzerInitPublic() {
+ FPDF_SetFuzzerPerProcessState(nullptr);
+}
diff --git a/testing/fuzzers/pdf_fuzzer_init_public.h b/testing/fuzzers/pdf_fuzzer_init_public.h
new file mode 100644
index 0000000..4b7f46f
--- /dev/null
+++ b/testing/fuzzers/pdf_fuzzer_init_public.h
@@ -0,0 +1,50 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TESTING_FUZZERS_PDF_FUZZER_INIT_PUBLIC_H_
+#define TESTING_FUZZERS_PDF_FUZZER_INIT_PUBLIC_H_
+
+#include <memory>
+
+#include "public/fpdf_ext.h"
+#include "public/fpdfview.h"
+
+#ifdef PDF_ENABLE_V8
+#include "fxjs/cfx_v8.h"
+#include "v8/include/v8-array-buffer.h"
+#include "v8/include/v8-snapshot.h"
+#endif // PDF_ENABLE_V8
+
+class XFAProcessState;
+
+#ifdef PDF_ENABLE_V8
+namespace v8 {
+class Isolate;
+class Platform;
+} // namespace v8
+#endif // PDF_ENABLE_V8
+
+// Initializes the library once for all runs of the fuzzer.
+class PDFFuzzerInitPublic {
+ public:
+ PDFFuzzerInitPublic();
+ ~PDFFuzzerInitPublic();
+
+ private:
+ FPDF_LIBRARY_CONFIG config_;
+ UNSUPPORT_INFO unsupport_info_;
+#ifdef PDF_ENABLE_V8
+#ifdef V8_USE_EXTERNAL_STARTUP_DATA
+ v8::StartupData snapshot_blob_;
+#endif // V8_USE_EXTERNAL_STARTUP_DATA
+ std::unique_ptr<v8::Platform> platform_;
+ std::unique_ptr<v8::ArrayBuffer::Allocator> allocator_;
+ std::unique_ptr<v8::Isolate, CFX_V8IsolateDeleter> isolate_;
+#ifdef PDF_ENABLE_XFA
+ std::unique_ptr<XFAProcessState> xfa_process_state_;
+#endif // PDF_ENABLE_XFA
+#endif // PDF_ENABLE_V8
+};
+
+#endif // TESTING_FUZZERS_PDF_FUZZER_INIT_PUBLIC_H_
diff --git a/testing/fuzzers/pdf_fuzzer_templates.h b/testing/fuzzers/pdf_fuzzer_templates.h
new file mode 100644
index 0000000..3fe4ea8
--- /dev/null
+++ b/testing/fuzzers/pdf_fuzzer_templates.h
@@ -0,0 +1,52 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// File for holding strings representing PDF templates that are used by fuzzers.
+
+#ifndef TESTING_FUZZERS_PDF_FUZZER_TEMPLATES_H_
+#define TESTING_FUZZERS_PDF_FUZZER_TEMPLATES_H_
+
+constexpr char kSimplePdfTemplate[] = R"(%PDF-1.7
+1 0 obj
+<</Type /Catalog /Pages 2 0 R /AcroForm <</XFA 30 0 R>> /NeedsRendering true>>
+endobj
+2 0 obj
+<</Type /Pages /Kids [3 0 R] /Count 1>>
+endobj
+3 0 obj
+<</Type /Page /Parent 2 0 R /MediaBox [0 0 3 3]>>
+endobj
+30 0 obj
+<</Length $1>>
+stream
+$2
+endstream
+endobj
+trailer
+<</Root 1 0 R /Size 31>>
+%%EOF)";
+
+// We define the bytes of the header explicitly to make the values more readable
+constexpr uint8_t kSimplePdfHeader[] = {0x25, 0x50, 0x44, 0x46, 0x2d,
+ 0x31, 0x2e, 0x37, 0x0a, 0x25,
+ 0xa0, 0xf2, 0xa4, 0xf4, 0x0a};
+
+constexpr char kCatalog[] = R""(<</AcroForm 2 0 R /Extensions
+ <</ADBE <</BaseVersion /1.7 /ExtensionLevel 8>>>> /NeedsRendering true
+ /Pages 3 0 R /Type /Catalog>>)"";
+
+constexpr char kSimpleXfaObjWrapper[] = R""(<</XFA
+ [(preamble) 5 0 R ($1) 6 0 R ($2) 7 0 R ($3) 8 0 R
+ (postamble) 9 0 R]>>)"";
+
+constexpr char kSimplePagesObj[] = "<</Count 1 /Kids [4 0 R] /Type /Pages>>";
+constexpr char kSimplePageObj[] =
+ "<</MediaBox [0 0 612 792] /Parent 3 0 R /Type /Page>>";
+constexpr char kSimplePreamble[] =
+ R""(<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/"
+timeStamp="2021-12-14T14:14:14Z"
+uuid="11111111-1ab1-11b1-aa1a-1aaaaaaa11a1">)"";
+constexpr char kSimplePostamble[] = "</xdp:xdp>";
+
+#endif // TESTING_FUZZERS_PDF_FUZZER_TEMPLATES_H_
diff --git a/testing/fuzzers/pdf_fx_date_helpers_fuzzer.cc b/testing/fuzzers/pdf_fx_date_helpers_fuzzer.cc
index e31decc..d98fffd 100644
--- a/testing/fuzzers/pdf_fx_date_helpers_fuzzer.cc
+++ b/testing/fuzzers/pdf_fx_date_helpers_fuzzer.cc
@@ -1,9 +1,7 @@
-// Copyright 2018 The PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include <string>
-
#include "core/fxcrt/widestring.h"
#include "fxjs/fx_date_helpers.h"
diff --git a/testing/fuzzers/pdf_hint_table_fuzzer.cc b/testing/fuzzers/pdf_hint_table_fuzzer.cc
index 1540074..3743b1a 100644
--- a/testing/fuzzers/pdf_hint_table_fuzzer.cc
+++ b/testing/fuzzers/pdf_hint_table_fuzzer.cc
@@ -1,4 +1,4 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -11,7 +11,6 @@
#include "core/fpdfapi/parser/cpdf_linearized_header.h"
#include "core/fpdfapi/parser/cpdf_number.h"
#include "core/fxcrt/cfx_bitstream.h"
-#include "third_party/base/ptr_util.h"
#include "third_party/base/span.h"
int32_t GetData(const int32_t** data32, const uint8_t** data, size_t* size) {
@@ -28,7 +27,7 @@
int shared_hint_table_offset)
: CPDF_HintTables(nullptr, pLinearized),
shared_hint_table_offset_(shared_hint_table_offset) {}
- ~HintTableForFuzzing() {}
+ ~HintTableForFuzzing() = default;
void Fuzz(const uint8_t* data, size_t size) {
if (shared_hint_table_offset_ <= 0)
@@ -76,9 +75,9 @@
auto hint_info = pdfium::MakeRetain<CPDF_Array>();
// Add primary hint stream offset
- hint_info->AddNew<CPDF_Number>(GetData(&data32, &data, &size));
+ hint_info->AppendNew<CPDF_Number>(GetData(&data32, &data, &size));
// Add primary hint stream size
- hint_info->AddNew<CPDF_Number>(GetData(&data32, &data, &size));
+ hint_info->AppendNew<CPDF_Number>(GetData(&data32, &data, &size));
// Set hint stream info.
linearized_dict->SetFor("H", std::move(hint_info));
diff --git a/testing/fuzzers/pdf_jpx_fuzzer.cc b/testing/fuzzers/pdf_jpx_fuzzer.cc
index 3986ae2..3021d76 100644
--- a/testing/fuzzers/pdf_jpx_fuzzer.cc
+++ b/testing/fuzzers/pdf_jpx_fuzzer.cc
@@ -1,17 +1,15 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
// 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 <vector>
#include "core/fpdfapi/page/cpdf_colorspace.h"
#include "core/fxcodec/jpx/cjpx_decoder.h"
-#include "core/fxcodec/jpx/jpxmodule.h"
#include "core/fxcrt/fx_safe_types.h"
#include "core/fxge/dib/cfx_dibitmap.h"
-#include "core/fxge/fx_dib.h"
+#include "core/fxge/dib/fx_dib.h"
namespace {
@@ -21,19 +19,19 @@
static constexpr uint32_t kMemLimitBytes = 1024 * 1024 * 1024; // 1 GB.
FX_SAFE_UINT32 mem = image_info.width;
mem *= image_info.height;
- mem *= image_info.components;
+ mem *= image_info.channels;
return mem.IsValid() && mem.ValueOrDie() <= kMemLimitBytes;
}
} // namespace
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
- if (size < 1)
+ if (size < 2)
return 0;
- std::unique_ptr<CJPX_Decoder> decoder = JpxModule::CreateDecoder(
- {data + 1, size - 1},
- static_cast<CJPX_Decoder::ColorSpaceOption>(data[0] % 3));
+ std::unique_ptr<CJPX_Decoder> decoder = CJPX_Decoder::Create(
+ {data + 2, size - 2},
+ static_cast<CJPX_Decoder::ColorSpaceOption>(data[0] % 3), data[1]);
if (!decoder)
return 0;
@@ -52,15 +50,15 @@
return 0;
FXDIB_Format format;
- if (image_info.components == 1) {
- format = FXDIB_8bppRgb;
- } else if (image_info.components <= 3) {
- format = FXDIB_Rgb;
- } else if (image_info.components == 4) {
- format = FXDIB_Rgb32;
+ if (image_info.channels == 1) {
+ format = FXDIB_Format::k8bppRgb;
+ } else if (image_info.channels <= 3) {
+ format = FXDIB_Format::kRgb;
+ } else if (image_info.channels == 4) {
+ format = FXDIB_Format::kRgb32;
} else {
- image_info.width = (image_info.width * image_info.components + 2) / 3;
- format = FXDIB_Rgb;
+ image_info.width = (image_info.width * image_info.channels + 2) / 3;
+ format = FXDIB_Format::kRgb;
}
auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
if (!bitmap->Create(image_info.width, image_info.height, format))
@@ -71,8 +69,8 @@
static_cast<uint32_t>(bitmap->GetHeight()))
return 0;
- decoder->Decode(bitmap->GetBuffer(), bitmap->GetPitch(),
- /*swap_rgb=*/false);
+ decoder->Decode(bitmap->GetBuffer(), bitmap->GetPitch(), /*swap_rgb=*/false,
+ GetCompsFromFormat(format));
return 0;
}
diff --git a/testing/fuzzers/pdf_lzw_fuzzer.cc b/testing/fuzzers/pdf_lzw_fuzzer.cc
index e4d993e..38d7929 100644
--- a/testing/fuzzers/pdf_lzw_fuzzer.cc
+++ b/testing/fuzzers/pdf_lzw_fuzzer.cc
@@ -1,10 +1,13 @@
-// Copyright 2017 The PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <stddef.h>
+#include <stdint.h>
+
#include <vector>
-#include "core/fxcodec/gif/cfx_lzwdecompressor.h"
+#include "core/fxcodec/gif/lzw_decompressor.h"
#include "third_party/base/numerics/safe_conversions.h"
// Between 2x and 5x is a standard range for LZW according to a quick
@@ -12,12 +15,14 @@
constexpr uint32_t kMinCompressionRatio = 2;
constexpr uint32_t kMaxCompressionRatio = 10;
+static constexpr size_t kMaxFuzzBytes = 1024 * 1024 * 1024; // 1 GB.
+
void LZWFuzz(const uint8_t* src_buf,
- size_t src_size,
+ uint32_t src_size,
uint8_t color_exp,
uint8_t code_exp) {
- std::unique_ptr<CFX_LZWDecompressor> decompressor =
- CFX_LZWDecompressor::Create(color_exp, code_exp);
+ std::unique_ptr<LZWDecompressor> decompressor =
+ LZWDecompressor::Create(color_exp, code_exp);
if (!decompressor)
return;
@@ -27,8 +32,9 @@
// This cast should be safe since the caller is checking for overflow on
// the initial data.
uint32_t dest_size = static_cast<uint32_t>(dest_buf.size());
- if (CFX_GifDecodeStatus::InsufficientDestSize !=
- decompressor->Decode(src_buf, src_size, dest_buf.data(), &dest_size)) {
+ decompressor->SetSource(src_buf, src_size);
+ if (LZWDecompressor::Status::kInsufficientDestSize !=
+ decompressor->Decode(dest_buf.data(), &dest_size)) {
return;
}
}
@@ -36,7 +42,7 @@
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
// Need at least 3 bytes to do anything.
- if (size < 3)
+ if (size < 3 || size > kMaxFuzzBytes)
return 0;
// Normally the GIF would provide the code and color sizes, instead, going
diff --git a/testing/fuzzers/pdf_nametree_fuzzer.cc b/testing/fuzzers/pdf_nametree_fuzzer.cc
index 9a37024..bc141ed 100644
--- a/testing/fuzzers/pdf_nametree_fuzzer.cc
+++ b/testing/fuzzers/pdf_nametree_fuzzer.cc
@@ -1,4 +1,4 @@
-// Copyright 2019 The PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -54,22 +54,23 @@
CPDF_StreamParser parser(remaining);
auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
- CPDF_NameTree name_tree(dict.Get());
+ std::unique_ptr<CPDF_NameTree> name_tree =
+ CPDF_NameTree::CreateForTesting(dict.Get());
for (const auto& name : params.names) {
RetainPtr<CPDF_Object> obj = parser.ReadNextObject(
/*bAllowNestedArray*/ true, /*bInArray=*/false, /*dwRecursionLevel=*/0);
if (!obj)
break;
- name_tree.AddValueAndName(std::move(obj), name);
+ name_tree->AddValueAndName(std::move(obj), name);
}
if (params.delete_backwards) {
for (size_t i = params.count; i > 0; --i)
- name_tree.DeleteValueAndName(i);
+ name_tree->DeleteValueAndName(i);
} else {
for (size_t i = 0; i < params.count; ++i)
- name_tree.DeleteValueAndName(0);
+ name_tree->DeleteValueAndName(0);
}
return 0;
}
diff --git a/testing/fuzzers/pdf_psengine_fuzzer.cc b/testing/fuzzers/pdf_psengine_fuzzer.cc
index d72088d..5cc2a76 100644
--- a/testing/fuzzers/pdf_psengine_fuzzer.cc
+++ b/testing/fuzzers/pdf_psengine_fuzzer.cc
@@ -1,8 +1,8 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include <cstdint>
+#include <stdint.h>
#include "core/fpdfapi/page/cpdf_psengine.h"
#include "third_party/base/span.h"
diff --git a/testing/fuzzers/pdf_scanlinecompositor_fuzzer.cc b/testing/fuzzers/pdf_scanlinecompositor_fuzzer.cc
index 4b20068..9c6e434 100644
--- a/testing/fuzzers/pdf_scanlinecompositor_fuzzer.cc
+++ b/testing/fuzzers/pdf_scanlinecompositor_fuzzer.cc
@@ -1,20 +1,36 @@
-// Copyright 2019 The PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <iterator>
+#include <memory>
+
+#include "core/fxcrt/fx_safe_types.h"
#include "core/fxge/cfx_cliprgn.h"
#include "core/fxge/dib/cfx_dibitmap.h"
-#include "core/fxge/fx_dib.h"
+#include "core/fxge/dib/fx_dib.h"
#include "testing/fuzzers/pdfium_fuzzer_util.h"
-#include "third_party/base/ptr_util.h"
namespace {
+// Some unused formats were removed, and their slots have been filled in
+// `FXDIB_Format::kInvalid` to keep the fuzzer input stable.
constexpr FXDIB_Format kFormat[] = {
- FXDIB_Invalid, FXDIB_1bppRgb, FXDIB_8bppRgb, FXDIB_Rgb,
- FXDIB_Rgb32, FXDIB_1bppMask, FXDIB_8bppMask, FXDIB_8bppRgba,
- FXDIB_Rgba, FXDIB_Argb, FXDIB_1bppCmyk, FXDIB_8bppCmyk,
- FXDIB_Cmyk, FXDIB_8bppCmyka, FXDIB_Cmyka};
+ FXDIB_Format::kInvalid,
+ FXDIB_Format::k1bppRgb,
+ FXDIB_Format::k8bppRgb,
+ FXDIB_Format::kRgb,
+ FXDIB_Format::kRgb32,
+ FXDIB_Format::k1bppMask,
+ FXDIB_Format::k8bppMask,
+ FXDIB_Format::kInvalid /* Was FXDIB_Format::k8bppRgba */,
+ FXDIB_Format::kInvalid /* Was FXDIB_Format::kRgba */,
+ FXDIB_Format::kArgb,
+ FXDIB_Format::kInvalid /* Was FXDIB_Format::k1bppCmyk */,
+ FXDIB_Format::kInvalid /* Was FXDIB_Format::k8bppCmyk */,
+ FXDIB_Format::kInvalid /* Was FXDIB_Format::kCmyk */,
+ FXDIB_Format::kInvalid /* Was FXDIB_Format::k8bppCmyka */,
+ FXDIB_Format::kInvalid /* Was FXDIB_Format::kCmyka */};
} // namespace
@@ -33,27 +49,35 @@
BlendMode blend_mode = static_cast<BlendMode>(
data[28] % (static_cast<int>(BlendMode::kLast) + 1));
- FXDIB_Format dest_format = kFormat[data[29] % FX_ArraySize(kFormat)];
- FXDIB_Format src_format = kFormat[data[30] % FX_ArraySize(kFormat)];
+ FXDIB_Format dest_format = kFormat[data[29] % std::size(kFormat)];
+ FXDIB_Format src_format = kFormat[data[30] % std::size(kFormat)];
bool is_clip = !(data[31] % 2);
bool is_rgb_byte_order = !(data[32] % 2);
size -= kParameterSize;
data += kParameterSize;
+ static constexpr uint32_t kMemLimit = 512000000; // 512 MB
+ static constexpr uint32_t kComponents = 4;
+ FX_SAFE_UINT32 mem = width;
+ mem *= height;
+ mem *= kComponents;
+ if (!mem.IsValid() || mem.ValueOrDie() > kMemLimit)
+ return 0;
+
auto src_bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
auto dest_bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
if (!src_bitmap->Create(width, height, src_format) ||
!dest_bitmap->Create(width, height, dest_format)) {
return 0;
}
- if (!src_bitmap->GetBuffer() || !dest_bitmap->GetBuffer()) {
+ if (src_bitmap->GetBuffer().empty() || dest_bitmap->GetBuffer().empty()) {
return 0;
}
std::unique_ptr<CFX_ClipRgn> clip_rgn;
if (is_clip)
- clip_rgn = pdfium::MakeUnique<CFX_ClipRgn>(width, height);
- if (src_bitmap->IsAlphaMask()) {
+ clip_rgn = std::make_unique<CFX_ClipRgn>(width, height);
+ if (src_bitmap->IsMaskFormat()) {
dest_bitmap->CompositeMask(dest_left, dest_top, width, height, src_bitmap,
argb, src_left, src_top, blend_mode,
clip_rgn.get(), is_rgb_byte_order);
diff --git a/testing/fuzzers/pdf_streamparser_fuzzer.cc b/testing/fuzzers/pdf_streamparser_fuzzer.cc
index 2bbda5e..e3bd787 100644
--- a/testing/fuzzers/pdf_streamparser_fuzzer.cc
+++ b/testing/fuzzers/pdf_streamparser_fuzzer.cc
@@ -1,9 +1,8 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
// 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 <stdint.h>
#include "core/fpdfapi/page/cpdf_streamparser.h"
#include "core/fpdfapi/parser/cpdf_object.h"
diff --git a/testing/fuzzers/pdf_xfa_fdp_fuzzer.cc b/testing/fuzzers/pdf_xfa_fdp_fuzzer.cc
new file mode 100644
index 0000000..c438c89
--- /dev/null
+++ b/testing/fuzzers/pdf_xfa_fdp_fuzzer.cc
@@ -0,0 +1,914 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include <string>
+#include <vector>
+
+#include "public/fpdf_formfill.h"
+#include "testing/fuzzers/pdf_fuzzer_templates.h"
+#include "testing/fuzzers/pdfium_fuzzer_helper.h"
+#include "third_party/base/containers/adapters.h"
+
+class PDFiumXFAFuzzer : public PDFiumFuzzerHelper {
+ public:
+ PDFiumXFAFuzzer() = default;
+ ~PDFiumXFAFuzzer() override = default;
+
+ int GetFormCallbackVersion() const override { return 2; }
+
+ void SetFdp(FuzzedDataProvider* fdp) { fdp_ = fdp; }
+
+ // Return false if XFA doesn't load as otherwise we're duplicating the work
+ // done by the non-xfa fuzzer.
+ bool OnFormFillEnvLoaded(FPDF_DOCUMENT doc) override {
+ int form_type = FPDF_GetFormType(doc);
+ if (form_type != FORMTYPE_XFA_FULL && form_type != FORMTYPE_XFA_FOREGROUND)
+ return false;
+ return FPDF_LoadXFA(doc);
+ }
+
+ void FormActionHandler(FPDF_FORMHANDLE form,
+ FPDF_DOCUMENT doc,
+ FPDF_PAGE page) override {
+ if (!fdp_) {
+ return;
+ }
+ char local_buf[50];
+ int number_of_calls = fdp_->ConsumeIntegralInRange<int>(0, 250);
+ for (int i = 0; i < number_of_calls; i++) {
+ UserInteraction selector = fdp_->ConsumeEnum<UserInteraction>();
+ switch (selector) {
+ case kOnLButtonUp: {
+ FORM_OnLButtonUp(form, page, fdp_->ConsumeIntegral<int>(),
+ fdp_->ConsumeIntegralInRange<int>(-100, 1000),
+ fdp_->ConsumeIntegralInRange<int>(-100, 1000));
+ break;
+ }
+ case kOnRButtonUp: {
+ FORM_OnRButtonUp(form, page, fdp_->ConsumeIntegral<int>(),
+ fdp_->ConsumeIntegralInRange<int>(-100, 1000),
+ fdp_->ConsumeIntegralInRange<int>(-100, 1000));
+ break;
+ }
+ case kOnLButtonDown: {
+ FORM_OnLButtonDown(form, page, fdp_->ConsumeIntegral<int>(),
+ fdp_->ConsumeIntegralInRange<int>(-100, 1000),
+ fdp_->ConsumeIntegralInRange<int>(-100, 1000));
+ break;
+ }
+ case kOnRButtonDown: {
+ FORM_OnRButtonDown(form, page, fdp_->ConsumeIntegral<int>(),
+ fdp_->ConsumeIntegralInRange<int>(-100, 1000),
+ fdp_->ConsumeIntegralInRange<int>(-100, 1000));
+ break;
+ }
+ case kOnChar: {
+ FORM_OnChar(form, page, fdp_->ConsumeIntegral<int>(),
+ fdp_->ConsumeIntegral<int>());
+ break;
+ }
+ case kOnKeyDown: {
+ FORM_OnKeyDown(form, page, fdp_->ConsumeIntegral<int>(),
+ fdp_->ConsumeIntegral<int>());
+ break;
+ }
+ case kOnKeyUp: {
+ FORM_OnKeyUp(form, page, fdp_->ConsumeIntegral<int>(),
+ fdp_->ConsumeIntegral<int>());
+ break;
+ }
+ case kOnLButtonDoubleClick: {
+ FORM_OnLButtonDoubleClick(form, page, fdp_->ConsumeIntegral<int>(),
+ fdp_->ConsumeIntegral<int>(),
+ fdp_->ConsumeIntegral<int>());
+ break;
+ }
+ case kOnMouseMove: {
+ FORM_OnMouseMove(form, page, fdp_->ConsumeIntegral<int>(),
+ fdp_->ConsumeIntegral<int>(),
+ fdp_->ConsumeIntegral<int>());
+ break;
+ }
+ case kOnMouseWheel: {
+ const FS_POINTF point = {fdp_->ConsumeFloatingPoint<float>(),
+ fdp_->ConsumeFloatingPoint<float>()};
+ FORM_OnMouseWheel(form, page, fdp_->ConsumeIntegral<int>(), &point,
+ fdp_->ConsumeIntegral<int>(),
+ fdp_->ConsumeIntegral<int>());
+ break;
+ }
+ case kOnFocus: {
+ FORM_OnFocus(form, page, fdp_->ConsumeIntegral<int>(),
+ fdp_->ConsumeIntegral<int>(),
+ fdp_->ConsumeIntegral<int>());
+ break;
+ }
+ case kUndo: {
+ if (FORM_CanUndo(form, page)) {
+ FORM_Undo(form, page);
+ }
+ break;
+ }
+ case kSelectAllText: {
+ FORM_SelectAllText(form, page);
+ break;
+ }
+ case kRedo: {
+ if (FORM_CanRedo(form, page)) {
+ FORM_Redo(form, page);
+ }
+ break;
+ }
+ case kAnnot: {
+ FPDF_ANNOTATION annot = nullptr;
+ int page_index = -2;
+ FORM_GetFocusedAnnot(form, &page_index, &annot);
+ if (annot) {
+ FORM_SetFocusedAnnot(form, annot);
+ }
+ break;
+ }
+ case kSetIndexSelected: {
+ FORM_SetIndexSelected(form, page, fdp_->ConsumeIntegral<int>(),
+ fdp_->ConsumeBool());
+ break;
+ }
+ case kIsIndexSelected: {
+ FORM_IsIndexSelected(form, page, fdp_->ConsumeIntegral<int>());
+ break;
+ }
+ case kHasFormFieldAtPoint: {
+ FPDFPage_HasFormFieldAtPoint(form, page, fdp_->ConsumeIntegral<int>(),
+ fdp_->ConsumeIntegral<int>());
+ break;
+ }
+ case kFormFieldZOrderAtPoint: {
+ FPDFPage_FormFieldZOrderAtPoint(form, page,
+ fdp_->ConsumeIntegral<int>(),
+ fdp_->ConsumeIntegral<int>());
+ break;
+ }
+ case kGetSelectedText: {
+ FORM_GetSelectedText(form, page, local_buf, sizeof(local_buf));
+ break;
+ }
+ case kGetFocusedText: {
+ FORM_GetFocusedText(form, page, local_buf, sizeof(local_buf));
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+ }
+ }
+
+ private:
+ enum UserInteraction {
+ kOnLButtonUp = 0,
+ kOnRButtonUp,
+ kOnLButtonDown,
+ kOnRButtonDown,
+ kOnChar,
+ kOnKeyDown,
+ kOnKeyUp,
+ kOnLButtonDoubleClick,
+ kOnMouseMove,
+ kOnMouseWheel,
+ kOnFocus,
+ kUndo,
+ kSelectAllText,
+ kRedo,
+ kAnnot,
+ kSetIndexSelected,
+ kIsIndexSelected,
+ kHasFormFieldAtPoint,
+ kFormFieldZOrderAtPoint,
+ kGetSelectedText,
+ kGetFocusedText,
+ kMaxValue = kGetFocusedText
+ };
+ FuzzedDataProvider* fdp_ = nullptr;
+};
+
+// Possible names of an XFA FormCalc script function
+std::string GenXfaFormCalcScriptFuncName(FuzzedDataProvider* data_provider) {
+ static const char* const kXfaScriptFuncs[] = {
+ "Abs", "Apr", "At", "Avg", "Ceil",
+ "Choose", "Concat", "Count", "Cterm", "Date",
+ "Date2Num", "DateFmt", "Decode", "Encode", "Eval",
+ "Exists", "Floor", "Format", "FV", "Get",
+ "HasValue", "If", "Ipmt", "IsoDate2Num", "IsoTime2Num",
+ "Left", "Len", "LocalDateFmt", "LocalTimeFmt", "Lower",
+ "Ltrim", "Max", "Min", "Mod", "NPV",
+ "Num2Date", "Num2GMTime", "Num2Time", "Oneof", "Parse",
+ "Pmt", "Post", "PPmt", "Put", "PV",
+ "Rate", "Ref", "Replace", "Right", "Round",
+ "Rtrim", "Space", "Str", "Stuff", "Substr",
+ "Sum", "Term", "Time", "Time2Num", "TimeFmt",
+ "Translate", "UnitType", "UnitValue", "Upper", "Uuid",
+ "Within", "WordNum",
+ };
+
+ size_t elem_selector = data_provider->ConsumeIntegralInRange<size_t>(
+ 0, std::size(kXfaScriptFuncs) - 1);
+ return kXfaScriptFuncs[elem_selector];
+}
+
+std::string MaybeQuote(FuzzedDataProvider* data_provider, std::string body) {
+ if (data_provider->ConsumeIntegralInRange<uint32_t>(0, 100) < 20) {
+ return "\"" + body + "\"";
+ }
+ return body;
+}
+
+// Possible arguments to a XFA script function
+std::string GenXfaScriptParam(FuzzedDataProvider* data_provider) {
+ static const char* const kXfaFuncParams[] = {
+ "$",
+ "-0",
+ "04/13/2019",
+ ".05",
+ "-1",
+ "1",
+ " 1 | 0",
+ "10 * 10 * 10 * 9 * 123",
+ "1024",
+ "10 * a + 9",
+ "1.2131",
+ "[1,2,3]",
+ "%123",
+ "[1,2,3][0]",
+ "123124",
+ "123342123",
+ "13:13:13",
+ "13:13:13 GMT",
+ "19960315T20:20:20",
+ "1 and 1",
+ "1 and 2",
+ "2",
+ "20000201",
+ "2009-06-01T13:45:30",
+ "2009-06-15T01:45:30",
+ "2009-06-15T13:45:30-07:00",
+ "2009-06-15T13:45:30.5275000",
+ " 2 < 3 + 1",
+ "2 + 3 + 9",
+ "3",
+ "3 * 1",
+ "3 -9",
+ "5 < 5",
+ "-99",
+ "99",
+ "9999999",
+ "99999999999",
+ "A",
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
+ "\xc3\x81\xc3\x82\xc3\x83\xc3\x84\xc3\x85\xc3\x86",
+ "<a><b></b></a>",
+ "Â",
+ "ÆÁÂÁ",
+ "Amount[*]",
+ "~!@#$%^&*()_+",
+ "&|",
+ "&apos",
+ "apr",
+ "april",
+ "B",
+ "<br>",
+ "C",
+ "de_DE",
+ "es_ES",
+ "feb",
+ "febuary",
+ "HH:MM:SS",
+ "<html>",
+ "html",
+ "HTML",
+ "jan",
+ "january",
+ "json",
+ "lkdjfglsdkfgj",
+ "mar",
+ "march",
+ "name[0]",
+ "name1",
+ "name2",
+ "name3",
+ "name4",
+ "name[*].numAmount",
+ """,
+ "Space",
+ "Str",
+ "url",
+ "xhtml",
+ "xml",
+ "XML"",
+ };
+
+ size_t elem_selector = data_provider->ConsumeIntegralInRange<size_t>(
+ 0, std::size(kXfaFuncParams) - 1);
+ return MaybeQuote(data_provider, kXfaFuncParams[elem_selector]);
+}
+
+// Possible XFA tags
+std::string GenXfaTag(FuzzedDataProvider* data_provider) {
+ static const char* const kXfaElemTags[] = {
+ "accessibleContent",
+ "acrobat",
+ "acrobat",
+ "acrobat7",
+ "ADBE_JSConsole",
+ "ADBE_JSDebugger",
+ "addSilentPrint",
+ "addViewerPreferences",
+ "adjustData",
+ "adobeExtensionLevel",
+ "agent",
+ "alwaysEmbed",
+ "amd",
+ "appearanceFilter",
+ "arc",
+ "area",
+ "assist",
+ "attributes",
+ "autoSave",
+ "barcode",
+ "base",
+ "batchOutput",
+ "behaviorOverride",
+ "bind",
+ "bindItems",
+ "bookend",
+ "boolean",
+ "border",
+ "break",
+ "breakAfter",
+ "breakBefore",
+ "button",
+ "cache",
+ "calculate",
+ "calendarSymbols",
+ "caption",
+ "certificate",
+ "certificates",
+ "change",
+ "checkButton",
+ "choiceList",
+ "color",
+ "comb",
+ "command",
+ "common",
+ "compress",
+ "compression",
+ "compressLogicalStructure",
+ "compressObjectStream",
+ "config",
+ "config",
+ "conformance",
+ "connect",
+ "connectionSet",
+ "connectString",
+ "contentArea",
+ "contentCopy",
+ "copies",
+ "corner",
+ "creator",
+ "currencySymbol",
+ "currencySymbols",
+ "currentPage",
+ "data",
+ "dataGroup",
+ "dataModel",
+ "dataValue",
+ "dataWindow",
+ "date",
+ "datePattern",
+ "datePatterns",
+ "dateTime",
+ "dateTimeEdit",
+ "dateTimeSymbols",
+ "day",
+ "dayNames",
+ "debug",
+ "decimal",
+ "defaultTypeface",
+ "defaultUi",
+ "delete",
+ "delta",
+ "deltas",
+ "desc",
+ "destination",
+ "digestMethod",
+ "digestMethods",
+ "documentAssembly",
+ "draw",
+ "driver",
+ "dSigData",
+ "duplexOption",
+ "dynamicRender",
+ "edge",
+ "effectiveInputPolicy",
+ "effectiveOutputPolicy",
+ "embed",
+ "encoding",
+ "encodings",
+ "encrypt",
+ "encryption",
+ "encryptionLevel",
+ "encryptionMethod",
+ "encryptionMethods",
+ "enforce",
+ "equate",
+ "equateRange",
+ "era",
+ "eraNames",
+ "event",
+ "eventPseudoModel",
+ "exclGroup",
+ "exclude",
+ "excludeNS",
+ "exData",
+ "execute",
+ "exObject",
+ "extras",
+ "field",
+ "fill",
+ "filter",
+ "flipLabel",
+ "float",
+ "font",
+ "fontInfo",
+ "form",
+ "format",
+ "formFieldFilling",
+ "groupParent",
+ "handler",
+ "hostPseudoModel",
+ "hyphenation",
+ "ifEmpty",
+ "image",
+ "imageEdit",
+ "includeXDPContent",
+ "incrementalLoad",
+ "incrementalMerge",
+ "insert",
+ "instanceManager",
+ "integer",
+ "interactive",
+ "issuers",
+ "items",
+ "jog",
+ "keep",
+ "keyUsage",
+ "labelPrinter",
+ "layout",
+ "layoutPseudoModel",
+ "level",
+ "line",
+ "linear",
+ "linearized",
+ "list",
+ "locale",
+ "localeSet",
+ "lockDocument",
+ "log",
+ "logPseudoModel",
+ "manifest",
+ "map",
+ "margin",
+ "mdp",
+ "medium",
+ "mediumInfo",
+ "meridiem",
+ "meridiemNames",
+ "message",
+ "messaging",
+ "mode",
+ "modifyAnnots",
+ "month",
+ "monthNames",
+ "msgId",
+ "nameAttr",
+ "neverEmbed",
+ "numberOfCopies",
+ "numberPattern",
+ "numberPatterns",
+ "numberSymbol",
+ "numberSymbols",
+ "numericEdit",
+ "object",
+ "occur",
+ "oid",
+ "oids",
+ "openAction",
+ "operation",
+ "output",
+ "outputBin",
+ "outputXSL",
+ "overflow",
+ "overprint",
+ "packet",
+ "packets",
+ "pageArea",
+ "pageOffset",
+ "pageRange",
+ "pageSet",
+ "pagination",
+ "paginationOverride",
+ "para",
+ "part",
+ "password",
+ "passwordEdit",
+ "pattern",
+ "pcl",
+ "pdf",
+ "pdfa",
+ "permissions",
+ "pickTrayByPDFSize",
+ "picture",
+ "plaintextMetadata",
+ "presence",
+ "present",
+ "present",
+ "print",
+ "printerName",
+ "printHighQuality",
+ "printScaling",
+ "producer",
+ "proto",
+ "ps",
+ "psMap",
+ "query",
+ "radial",
+ "range",
+ "reason",
+ "reasons",
+ "record",
+ "recordSet",
+ "rectangle",
+ "ref",
+ "relevant",
+ "rename",
+ "renderPolicy",
+ "rootElement",
+ "runScripts",
+ "script",
+ "scriptModel",
+ "select",
+ "setProperty",
+ "severity",
+ "signature",
+ "signatureProperties",
+ "signaturePseudoModel",
+ "signData",
+ "signing",
+ "silentPrint",
+ "soapAction",
+ "soapAddress",
+ "solid",
+ "source",
+ "sourceSet",
+ "speak",
+ "staple",
+ "startNode",
+ "startPage",
+ "stipple",
+ "subform",
+ "subform",
+ "subformSet",
+ "subjectDN",
+ "subjectDNs",
+ "submit",
+ "submitFormat",
+ "submitUrl",
+ "subsetBelow",
+ "suppressBanner",
+ "tagged",
+ "template",
+ "template",
+ "templateCache",
+ "#text",
+ "text",
+ "textedit",
+ "textEdit",
+ "threshold",
+ "time",
+ "timePattern",
+ "timePatterns",
+ "timeStamp",
+ "to",
+ "toolTip",
+ "trace",
+ "transform",
+ "traversal",
+ "traverse",
+ "treeList",
+ "type",
+ "typeface",
+ "typefaces",
+ "ui",
+ "update",
+ "uri",
+ "user",
+ "validate",
+ "validate",
+ "validateApprovalSignatures",
+ "validationMessaging",
+ "value",
+ "variables",
+ "version",
+ "versionControl",
+ "viewerPreferences",
+ "webClient",
+ "whitespace",
+ "window",
+ "wsdlAddress",
+ "wsdlConnection",
+ "xdc",
+ "xdp",
+ "xfa",
+ "#xHTML",
+ "#xml",
+ "xmlConnection",
+ "xsdConnection",
+ "xsl",
+ "zpl",
+ };
+
+ size_t elem_selector = data_provider->ConsumeIntegralInRange<size_t>(
+ 0, std::size(kXfaElemTags) - 1);
+ return kXfaElemTags[elem_selector];
+}
+
+// Possible XFA attributes values
+std::string GenXfaTagValue(FuzzedDataProvider* data_provider) {
+ static const char* const kXfaTagVals[] = {
+ "0", "0pt", "-1",
+ "123", "1pt", "203.2mm",
+ "22.1404mm", "255", "256",
+ "321", "5431.21mm", "6.35mm",
+ "8in", "8pt", "application/x-javascript",
+ "bold", "bold", "change",
+ "click", "consumeData", "docReady",
+ "en_US", "form1", "initialize",
+ "italic", "middle", "name2",
+ "name3", "name4", "name5",
+ "onEnter", "Page1", "RadioList[0]",
+ "subform_1", "tb", "Verdana",
+ };
+
+ size_t elem_selector = data_provider->ConsumeIntegralInRange<size_t>(
+ 0, std::size(kXfaTagVals) - 1);
+ return MaybeQuote(data_provider, kXfaTagVals[elem_selector]);
+}
+
+// possible XFA attributes
+std::string GenXfaTagName(FuzzedDataProvider* data_provider) {
+ static const char* const kXfaTagNames[] = {
+ "activity", "baselineShift",
+ "contentType", "h",
+ "id", "layout",
+ "layout", "leftInset",
+ "locale", "long",
+ "marginLeft", "marginRight",
+ "marginRight", "mergeMode",
+ "name", "ref",
+ "scriptTest", "short",
+ "size", "spaceAbove",
+ "spaceBelow", "startNew",
+ "stock", "textIndent",
+ "timeStamp", "typeface",
+ "uuid", "vAlign",
+ "value", "w",
+ "weight", "x",
+ "y",
+ };
+ size_t elem_selector = data_provider->ConsumeIntegralInRange<size_t>(
+ 0, std::size(kXfaTagNames) - 1);
+ return kXfaTagNames[elem_selector];
+}
+
+// Will create a simple XFA FormCalc script that calls a single function.
+std::string GenXfaFormCalcScript(FuzzedDataProvider* data_provider) {
+ std::string xfa_string = GenXfaFormCalcScriptFuncName(data_provider);
+ xfa_string += "(";
+
+ // Generate parameters
+ size_t num_params = data_provider->ConsumeIntegralInRange<size_t>(0, 3);
+ for (size_t i = 0; i < num_params; i++) {
+ if (i != 0) {
+ xfa_string += ",";
+ }
+ xfa_string += GenXfaScriptParam(data_provider);
+ }
+ xfa_string += ")";
+ return xfa_string;
+}
+
+// XFA Javascript logic
+std::string GenXfaName(FuzzedDataProvider* data_provider) {
+ return "name" + std::to_string(data_provider->ConsumeIntegralInRange(0, 25));
+}
+
+std::string GetXfaJSPrimitiveType(FuzzedDataProvider* data_provider) {
+ return GenXfaScriptParam(data_provider);
+}
+
+std::string GenXfaJSRValue(FuzzedDataProvider* data_provider) {
+ if (data_provider->ConsumeBool()) {
+ return GenXfaScriptParam(data_provider);
+ }
+
+ std::string xfa_string;
+ if (data_provider->ConsumeBool()) {
+ xfa_string += "xfa.form.";
+ }
+
+ // Handle the possibility of nested names
+ size_t num_nests = data_provider->ConsumeIntegralInRange<size_t>(1, 3);
+ for (size_t i = 0; i < num_nests; i++) {
+ if (i != 0) {
+ xfa_string += ".";
+ }
+ xfa_string += GenXfaName(data_provider);
+ }
+ return MaybeQuote(data_provider, xfa_string);
+}
+
+std::string GenXfaJSAssignment(FuzzedDataProvider* data_provider) {
+ return GenXfaName(data_provider) + " = " + GenXfaJSRValue(data_provider);
+}
+
+std::string GenXfaJSMethodCall(FuzzedDataProvider* data_provider) {
+ static const char* const kXfaJSFuncs[] = {
+ "addItem",
+ "boundItem",
+ "clearItems",
+ "deleteItem",
+ "execCalculate",
+ "execEvent",
+ "execInitialize",
+ "execValidate",
+ "getDisplayItem",
+ "getItemState",
+ "getSaveItem",
+ "exec.form.formNodes",
+ "exec.form.recalculate",
+ "setItemState",
+ "xfa.container.getDelta",
+ "xfa.container.getDeltas",
+ "xfa.event.emit",
+ "xfa.event.reset",
+ "xfa.form.execCalculat",
+ "xfa.form.execInitialize",
+ "xfa.form.execValidate",
+ "xfa.form.remerge",
+ "xfa.host.beep",
+ "xfa.host.documentCountInBatch",
+ "xfa.host.documentInBatch",
+ "xfa.host.exportData",
+ "xfa.host.getFocus",
+ "xfa.host.gotoURL",
+ "xfa.host.importData",
+ "xfa.host.messageBox",
+ "xfa.host.openList",
+ "xfa.host.pageDown",
+ "xfa.host.pageUp",
+ "xfa.host.print",
+ "xfa.host.resetData",
+ "xfa.host.setFocus",
+ "xfa.host.response",
+ "xfa.resolveNode",
+ };
+
+ std::string xfa_string = data_provider->PickValueInArray(kXfaJSFuncs);
+ xfa_string += "(";
+
+ // Get the params
+ size_t param_count = data_provider->ConsumeIntegralInRange<size_t>(0, 3);
+ for (size_t i = 0; i < param_count; i++) {
+ if (i != 0) {
+ xfa_string += ",";
+ }
+ xfa_string += GenXfaJSRValue(data_provider);
+ }
+ xfa_string += ")";
+ return xfa_string;
+}
+
+// This is a simple generator of xfa-based javascript. The function creates
+// simple javascript statements that are related to XFA logic and the goal is
+// not to create fully-fleged javascript programs but rather use simple
+// statements to ensure XFA code is covered.
+enum XFAJSStatement {
+ kAssignment = 0,
+ kJSMethodCall,
+ kJSObjectCall,
+ kMaxValue = kJSObjectCall
+};
+
+std::string GenXfaJSScript(FuzzedDataProvider* data_provider) {
+ std::string xfa_string;
+
+ size_t num_stmts = data_provider->ConsumeIntegralInRange<size_t>(1, 10);
+ for (size_t i = 0; i < num_stmts; i++) {
+ XFAJSStatement stmt = data_provider->ConsumeEnum<XFAJSStatement>();
+ switch (stmt) {
+ case kAssignment:
+ xfa_string += GenXfaJSAssignment(data_provider);
+ break;
+ case kJSMethodCall:
+ xfa_string += GenXfaJSMethodCall(data_provider);
+ break;
+ case kJSObjectCall:
+ xfa_string += GenXfaName(data_provider);
+ xfa_string += ".";
+ xfa_string += GenXfaJSMethodCall(data_provider);
+ break;
+ }
+ xfa_string += ";\n";
+ }
+ return xfa_string;
+}
+
+std::string GenXfacript(FuzzedDataProvider* data_provider) {
+ // Determine if this should be a FormCalc script or Javascript, 50/50 chance
+ // for each.
+ if (data_provider->ConsumeBool()) {
+ return GenXfaFormCalcScript(data_provider);
+ }
+ return GenXfaJSScript(data_provider);
+}
+
+// Will create a single XFA attributes, with both lhs and rhs.
+std::string getXfaElemAttributes(FuzzedDataProvider* data_provider) {
+ // Generate a set of tags, and a set of values for the tags.
+ return GenXfaTagName(data_provider) + "=" + GenXfaTagValue(data_provider);
+}
+
+// Creates an XFA structure wrapped in <xdp tags.
+std::string GenXfaTree(FuzzedDataProvider* data_provider) {
+ std::string xfa_string = "<xdp xmlns=\"http://ns.adobe.com/xdp/\">";
+
+ // One stack iteration
+ int stack_iterations = data_provider->ConsumeIntegralInRange(1, 3);
+ for (int si = 0; si < stack_iterations; si++) {
+ int elem_count = data_provider->ConsumeIntegralInRange(1, 6);
+ std::vector<std::string> xml_stack;
+ xml_stack.reserve(elem_count);
+ for (int i = 0; i < elem_count; i++) {
+ std::string tag = GenXfaTag(data_provider);
+ xfa_string += "<" + tag;
+
+ // in 30% of cases, add attributes
+ if (data_provider->ConsumeIntegralInRange(1, 100) > 70) {
+ size_t attribute_count = data_provider->ConsumeIntegralInRange(1, 5);
+ for (; 0 < attribute_count; attribute_count--) {
+ xfa_string += " " + getXfaElemAttributes(data_provider);
+ }
+ }
+ xfa_string += ">";
+
+ // If needed, add a body to the tag
+ if (tag == "script") {
+ xfa_string += GenXfacript(data_provider);
+ }
+
+ // Push the tag to the stack so we can close it when done
+ xml_stack.push_back(tag);
+ }
+ for (const std::string& tag : pdfium::base::Reversed(xml_stack)) {
+ xfa_string += "</" + tag + ">";
+ }
+ }
+ xfa_string += "</xdp>";
+ return xfa_string;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider data_provider(data, size);
+ std::string xfa_string = GenXfaTree(&data_provider);
+
+ // Add 1 for newline before endstream.
+ std::string xfa_stream_len = std::to_string(xfa_string.size() + 1);
+
+ // Compose the fuzzer
+ std::string xfa_final_str = std::string(kSimplePdfTemplate);
+ xfa_final_str.replace(xfa_final_str.find("$1"), 2, xfa_stream_len);
+ xfa_final_str.replace(xfa_final_str.find("$2"), 2, xfa_string);
+
+#ifdef PDFIUM_FUZZER_DUMP
+ for (size_t i = 0; i < xfa_final_str.size(); i++) {
+ putc(xfa_final_str[i], stdout);
+ }
+#endif
+
+ PDFiumXFAFuzzer fuzzer;
+ fuzzer.SetFdp(&data_provider);
+ fuzzer.RenderPdf(xfa_final_str.c_str(), xfa_final_str.size());
+ return 0;
+}
diff --git a/testing/fuzzers/pdf_xfa_raw_fuzzer.cc b/testing/fuzzers/pdf_xfa_raw_fuzzer.cc
new file mode 100644
index 0000000..8dce02f
--- /dev/null
+++ b/testing/fuzzers/pdf_xfa_raw_fuzzer.cc
@@ -0,0 +1,101 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include <cctype>
+#include <string>
+
+#include "public/fpdf_formfill.h"
+#include "testing/fuzzers/pdf_fuzzer_templates.h"
+#include "testing/fuzzers/pdfium_fuzzer_helper.h"
+
+class PDFiumXFAFuzzer : public PDFiumFuzzerHelper {
+ public:
+ PDFiumXFAFuzzer() = default;
+ ~PDFiumXFAFuzzer() override = default;
+
+ int GetFormCallbackVersion() const override { return 2; }
+
+ // Return false if XFA doesn't load as otherwise we're duplicating the work
+ // done by the non-xfa fuzzer.
+ bool OnFormFillEnvLoaded(FPDF_DOCUMENT doc) override {
+ int form_type = FPDF_GetFormType(doc);
+ if (form_type != FORMTYPE_XFA_FULL && form_type != FORMTYPE_XFA_FOREGROUND)
+ return false;
+ return FPDF_LoadXFA(doc);
+ }
+};
+
+bool IsValidForFuzzing(const uint8_t* data, size_t size) {
+ if (size > 2048) {
+ return false;
+ }
+
+ const char* ptr = reinterpret_cast<const char*>(data);
+ bool is_open = false;
+ size_t tag_size = 0;
+ for (size_t i = 0; i < size; i++) {
+ if (!std::isspace(ptr[i]) && !std::isprint(ptr[i])) {
+ return false;
+ }
+
+ // We do not want any script tags. The reason is this fuzzer
+ // should avoid exploring v8 code. Avoiding anything with "script"
+ // is an over-approximation, in that some inputs may contain "script"
+ // and still be a valid fuzz-case. However, this over-approximation is
+ // used to enforce strict constraints and avoid cases where whitespace
+ // may play a role, or other tags, e.g. "Javascript" will end up triggering
+ // large explorations of v8 code. The alternative we considered were
+ // "<script"
+ if (i + 6 < size && memcmp(ptr + i, "script", 6) == 0) {
+ return false;
+ }
+
+ if (ptr[i] == '<') {
+ if (is_open) {
+ return false;
+ }
+ is_open = true;
+ tag_size = 0;
+ } else if (ptr[i] == '>') {
+ if (!is_open || tag_size == 0) {
+ return false;
+ }
+ is_open = false;
+ } else if (is_open) {
+ tag_size++;
+ }
+ }
+ // we must close the last bracket.
+ return !is_open;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ // Filter the string to reduce the state space exploration.
+ if (!IsValidForFuzzing(data, size)) {
+ return 0;
+ }
+ std::string xfa_string = "<xdp xmlns=\"http://ns.adobe.com/xdp/\">";
+ xfa_string += std::string(reinterpret_cast<const char*>(data), size);
+ xfa_string += "</xdp>";
+
+ // Add 1 for newline before endstream.
+ std::string xfa_stream_len = std::to_string(xfa_string.size() + 1);
+
+ // Compose the fuzzer
+ std::string xfa_final_str = std::string(kSimplePdfTemplate);
+ xfa_final_str.replace(xfa_final_str.find("$1"), 2, xfa_stream_len);
+ xfa_final_str.replace(xfa_final_str.find("$2"), 2, xfa_string);
+
+#ifdef PDFIUM_FUZZER_DUMP
+ for (size_t i = 0; i < xfa_final_str.size(); i++) {
+ putc(xfa_final_str[i], stdout);
+ }
+#endif
+
+ PDFiumXFAFuzzer fuzzer;
+ fuzzer.RenderPdf(xfa_final_str.c_str(), xfa_final_str.size());
+ return 0;
+}
diff --git a/testing/fuzzers/pdf_xfa_xdp_fdp_fuzzer.cc b/testing/fuzzers/pdf_xfa_xdp_fdp_fuzzer.cc
new file mode 100644
index 0000000..8917ce7
--- /dev/null
+++ b/testing/fuzzers/pdf_xfa_xdp_fdp_fuzzer.cc
@@ -0,0 +1,206 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include <string>
+
+#include "public/fpdf_formfill.h"
+#include "testing/fuzzers/pdf_fuzzer_templates.h"
+#include "testing/fuzzers/pdfium_fuzzer_helper.h"
+
+class PDFiumXDPFuzzer : public PDFiumFuzzerHelper {
+ public:
+ PDFiumXDPFuzzer() = default;
+ ~PDFiumXDPFuzzer() override = default;
+
+ int GetFormCallbackVersion() const override { return 2; }
+
+ bool OnFormFillEnvLoaded(FPDF_DOCUMENT doc) override {
+ int form_type = FPDF_GetFormType(doc);
+ if (form_type != FORMTYPE_XFA_FULL && form_type != FORMTYPE_XFA_FOREGROUND)
+ return false;
+ return FPDF_LoadXFA(doc);
+ }
+};
+
+struct Tag {
+ const char* tag_name;
+ const char* tag_start;
+ const char* tag_end;
+};
+
+const Tag kTagData[]{
+ {.tag_name = "config",
+ .tag_start =
+ R""(<xfa:config xmlns:xfa="http://www.xfa.org/schema/xci/3.1/">)"",
+ .tag_end = "</xfa:config>"},
+ {.tag_name = "template",
+ .tag_start =
+ R""(<template xmlns="http://www.xfa.org/schema/xfa-template/2.6/">)"",
+ .tag_end = "</template>"},
+ {.tag_name = "sourceSet",
+ .tag_start =
+ R""(<sourceSet xmlns="http://www.xfa.org/schema/xfa-source-set/2.7/">)"",
+ .tag_end = "</sourceSet>"},
+ {.tag_name = "localeSet",
+ .tag_start =
+ R""(<localeSet xmlns="http://www.xfa.org/schema/xfa-locale-set/2.7/">)"",
+ .tag_end = "</localeSet>"},
+ {.tag_name = "dataSet",
+ .tag_start =
+ R""(<xfa:datasets xmlns:xfa="http://www.xfa.org/schema/xfa-data/1.0/">)"",
+ .tag_end = "</xfa:datasets>"},
+ {.tag_name = "connectionSet",
+ .tag_start =
+ R""(<connectionSet xmlns="http://www.xfa.org/schema/xfa-connection-set/2.8/">)"",
+ .tag_end = "</connectionSet>"},
+ {.tag_name = "xdc",
+ .tag_start =
+ R""(<xsl:xdc xmlns:xdc="http://www.xfa.org/schema/xdc/1.0/">)"",
+ .tag_end = "</xsl:xdc>"},
+ {.tag_name = "signature",
+ .tag_start = R""(<signature xmlns="http://www.w3.org/2000/09/xmldsig#">)"",
+ .tag_end = "</signature>"},
+ {.tag_name = "stylesheet",
+ .tag_start =
+ R""(<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" id="identifier">)"",
+ .tag_end = "</stylesheet>"},
+ {.tag_name = "xfdf",
+ .tag_start =
+ R""(<xfdf xmlns="http://ns.adobe.com/xfdf/" xml:space="preserve">)"",
+ .tag_end = "</xfdf>"},
+ {.tag_name = "xmpmeta",
+ .tag_start =
+ R""(<xmpmeta xmlns="http://ns.adobe.com/xmpmeta/" xml:space="preserve">)"",
+ .tag_end = "</xmpmeta>"}};
+
+std::string CreateObject(int obj_num, const std::string& body) {
+ std::string obj_template = R""($1 0 obj
+$2
+endobj
+)"";
+
+ obj_template.replace(obj_template.find("$1"), 2, std::to_string(obj_num));
+ obj_template.replace(obj_template.find("$2"), 2, body);
+ return obj_template;
+}
+
+std::string CreateStreamObject(int obj_num, const std::string& body) {
+ std::string obj_template = R""($1 0 obj
+<</Length $2>>
+stream
+$3
+endstream
+endobj
+)"";
+
+ obj_template.replace(obj_template.find("$1"), 2, std::to_string(obj_num));
+ obj_template.replace(obj_template.find("$2"), 2,
+ std::to_string(body.size() + 1));
+ obj_template.replace(obj_template.find("$3"), 2, body);
+
+ return obj_template;
+}
+
+std::string GenXrefEntry(size_t offset) {
+ return std::string(10 - std::to_string(offset).size(), '0') +
+ std::to_string(offset) + " 00000 n\n";
+}
+
+std::string GenTagBody(const Tag& tag, FuzzedDataProvider* data_provider) {
+ std::string tag_content = data_provider->ConsumeRandomLengthString();
+ return tag.tag_start + tag_content + tag.tag_end;
+}
+
+std::string GenXDPPdfFile(FuzzedDataProvider* data_provider) {
+ std::vector<std::string> pdf_objects;
+ std::string pdf_header =
+ std::string(reinterpret_cast<const char*>(kSimplePdfHeader),
+ sizeof(kSimplePdfHeader));
+
+ pdf_objects.push_back(CreateObject(1, kCatalog));
+
+ std::string xfa_obj = kSimpleXfaObjWrapper;
+ Tag tag1 = data_provider->PickValueInArray(kTagData);
+ Tag tag2 = data_provider->PickValueInArray(kTagData);
+ Tag tag3 = data_provider->PickValueInArray(kTagData);
+ xfa_obj.replace(xfa_obj.find("$1"), 2, tag1.tag_name);
+ xfa_obj.replace(xfa_obj.find("$2"), 2, tag2.tag_name);
+ xfa_obj.replace(xfa_obj.find("$3"), 2, tag3.tag_name);
+ pdf_objects.push_back(CreateObject(2, xfa_obj));
+ pdf_objects.push_back(CreateObject(3, kSimplePagesObj));
+ pdf_objects.push_back(CreateObject(4, kSimplePageObj));
+
+ // preamble
+ pdf_objects.push_back(CreateStreamObject(5, kSimplePreamble));
+
+ // The three XFA tags
+ pdf_objects.push_back(CreateStreamObject(6, GenTagBody(tag1, data_provider)));
+ pdf_objects.push_back(CreateStreamObject(7, GenTagBody(tag2, data_provider)));
+ pdf_objects.push_back(CreateStreamObject(8, GenTagBody(tag3, data_provider)));
+
+ // postamble
+ pdf_objects.push_back(CreateStreamObject(9, kSimplePostamble));
+
+ // Create the xref table
+ std::string xref = R""(xref
+0 10
+0000000000 65535 f
+)"";
+
+ // Add xref entries
+ size_t curr_offset = pdf_header.size();
+ for (const auto& ostr : pdf_objects) {
+ xref += GenXrefEntry(curr_offset);
+ curr_offset += ostr.size();
+ }
+
+ std::string footer = R""(trailer
+<</Root 1 0 R /Size 10>>
+startxref
+$1
+%%EOF)"";
+ footer.replace(footer.find("$1"), 2, std::to_string(curr_offset));
+
+ std::string pdf_core;
+ for (const auto& ostr : pdf_objects) {
+ pdf_core += ostr;
+ }
+
+ // Return the full PDF
+ return pdf_header + pdf_core + xref + footer;
+}
+
+bool IsValidForFuzzing(const uint8_t* data, size_t size) {
+ if (size > 2048) {
+ return false;
+ }
+ const char* ptr = reinterpret_cast<const char*>(data);
+ for (size_t i = 0; i < size; i++) {
+ if (!std::isspace(ptr[i]) && !std::isprint(ptr[i])) {
+ return false;
+ }
+ }
+ return true;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ if (!IsValidForFuzzing(data, size)) {
+ return 0;
+ }
+
+ FuzzedDataProvider data_provider(data, size);
+ std::string xfa_final_str = GenXDPPdfFile(&data_provider);
+
+#ifdef PDFIUM_FUZZER_DUMP
+ for (size_t i = 0; i < xfa_final_str.size(); i++) {
+ putc(xfa_final_str[i], stdout);
+ }
+#endif
+
+ PDFiumXDPFuzzer fuzzer;
+ fuzzer.RenderPdf(xfa_final_str.c_str(), xfa_final_str.size());
+ return 0;
+}
diff --git a/testing/fuzzers/pdf_xml_fuzzer.cc b/testing/fuzzers/pdf_xml_fuzzer.cc
index e858f5b..31ab6d7 100644
--- a/testing/fuzzers/pdf_xml_fuzzer.cc
+++ b/testing/fuzzers/pdf_xml_fuzzer.cc
@@ -1,4 +1,4 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -6,7 +6,7 @@
#include <cstdint>
#include <memory>
-#include "core/fxcrt/cfx_readonlymemorystream.h"
+#include "core/fxcrt/cfx_read_only_span_stream.h"
#include "core/fxcrt/fx_safe_types.h"
#include "core/fxcrt/fx_system.h"
#include "core/fxcrt/xml/cfx_xmldocument.h"
@@ -19,8 +19,8 @@
if (!safe_size.IsValid())
return 0;
- auto stream = pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
- pdfium::make_span(data, size));
+ auto stream =
+ pdfium::MakeRetain<CFX_ReadOnlySpanStream>(pdfium::make_span(data, size));
CFX_XMLParser parser(stream);
std::unique_ptr<CFX_XMLDocument> doc = parser.Parse();
if (!doc || !doc->GetRoot())
diff --git a/testing/fuzzers/pdfium_fuzzer.cc b/testing/fuzzers/pdfium_fuzzer.cc
index dc15378..7e25fed 100644
--- a/testing/fuzzers/pdfium_fuzzer.cc
+++ b/testing/fuzzers/pdfium_fuzzer.cc
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -6,6 +6,12 @@
#include "testing/fuzzers/pdfium_fuzzer_helper.h"
+#if defined(BUILD_WITH_CHROMIUM)
+#include "base/memory/discardable_memory_allocator.h"
+#include "base/no_destructor.h"
+#include "base/test/test_discardable_memory_allocator.h"
+#endif
+
class PDFiumFuzzer : public PDFiumFuzzerHelper {
public:
PDFiumFuzzer() = default;
@@ -15,6 +21,12 @@
};
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+#if defined(BUILD_WITH_CHROMIUM)
+ static base::NoDestructor<base::TestDiscardableMemoryAllocator>
+ test_memory_allocator;
+ base::DiscardableMemoryAllocator::SetInstance(test_memory_allocator.get());
+#endif
+
PDFiumFuzzer fuzzer;
fuzzer.RenderPdf(reinterpret_cast<const char*>(data), size);
return 0;
diff --git a/testing/fuzzers/pdfium_fuzzer_helper.cc b/testing/fuzzers/pdfium_fuzzer_helper.cc
index 266666d..f011b18 100644
--- a/testing/fuzzers/pdfium_fuzzer_helper.cc
+++ b/testing/fuzzers/pdfium_fuzzer_helper.cc
@@ -1,19 +1,16 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "testing/fuzzers/pdfium_fuzzer_helper.h"
#include <assert.h>
-#include <limits.h>
-
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <memory>
#include <sstream>
#include <string>
#include <tuple>
@@ -23,6 +20,7 @@
#include "public/fpdf_dataavail.h"
#include "public/fpdf_ext.h"
#include "public/fpdf_text.h"
+#include "third_party/base/notreached.h"
#include "third_party/base/span.h"
namespace {
@@ -95,7 +93,7 @@
std::pair<int, int> GetRenderingAndFormFlagFromData(const char* data,
size_t len) {
std::string data_str = std::string(data, len);
- std::size_t data_hash = std::hash<std::string>()(data_str);
+ size_t data_hash = std::hash<std::string>()(data_str);
// The largest flag value is 0x4FFF, so just take 16 bits from |data_hash| at
// a time.
@@ -218,6 +216,8 @@
FORM_OnAfterLoadPage(page.get(), form);
FORM_DoPageAAction(page.get(), form, FPDFPAGE_AACTION_OPEN);
+ FormActionHandler(form, doc, page.get());
+
const double scale = 1.0;
int width = static_cast<int>(FPDF_GetPageWidthF(page.get()) * scale);
int height = static_cast<int>(FPDF_GetPageHeightF(page.get()) * scale);
diff --git a/testing/fuzzers/pdfium_fuzzer_helper.h b/testing/fuzzers/pdfium_fuzzer_helper.h
index af14941..900f55d 100644
--- a/testing/fuzzers/pdfium_fuzzer_helper.h
+++ b/testing/fuzzers/pdfium_fuzzer_helper.h
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -16,6 +16,9 @@
virtual int GetFormCallbackVersion() const = 0;
virtual bool OnFormFillEnvLoaded(FPDF_DOCUMENT doc);
virtual void OnRenderFinished(FPDF_DOCUMENT doc) {}
+ virtual void FormActionHandler(FPDF_FORMHANDLE form,
+ FPDF_DOCUMENT doc,
+ FPDF_PAGE page) {}
protected:
PDFiumFuzzerHelper();
diff --git a/testing/fuzzers/pdfium_fuzzer_util.cc b/testing/fuzzers/pdfium_fuzzer_util.cc
index 9238f0e..f8f8286 100644
--- a/testing/fuzzers/pdfium_fuzzer_util.cc
+++ b/testing/fuzzers/pdfium_fuzzer_util.cc
@@ -1,9 +1,21 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "testing/fuzzers/pdfium_fuzzer_util.h"
+namespace {
+void* g_fuzzer_init_per_process_state = nullptr;
+} // namespace
+
int GetInteger(const uint8_t* data) {
return data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
}
+
+FPDF_EXPORT void FPDF_CALLCONV FPDF_SetFuzzerPerProcessState(void* state) {
+ g_fuzzer_init_per_process_state = state;
+}
+
+FPDF_EXPORT void* FPDF_CALLCONV FPDF_GetFuzzerPerProcessState() {
+ return g_fuzzer_init_per_process_state;
+}
diff --git a/testing/fuzzers/pdfium_fuzzer_util.h b/testing/fuzzers/pdfium_fuzzer_util.h
index d82f44b..a37544d 100644
--- a/testing/fuzzers/pdfium_fuzzer_util.h
+++ b/testing/fuzzers/pdfium_fuzzer_util.h
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -7,7 +7,19 @@
#include <stdint.h>
+#include "public/fpdfview.h"
+
// Returns an integer from the first 4 bytes of |data|.
int GetInteger(const uint8_t* data);
+// Plumb access to any context created by fuzzer initialization into
+// the LLVMFuzzerTestOneInput() function, as that function does not
+// allow for additional parameters, nor can it reach back up to the
+// top-level fuzzer shim during a component build (see the comment
+// in BUILD.gn about splitting fuzzers into _impl and _src targets).
+extern "C" {
+FPDF_EXPORT void FPDF_CALLCONV FPDF_SetFuzzerPerProcessState(void* state);
+FPDF_EXPORT void* FPDF_CALLCONV FPDF_GetFuzzerPerProcessState();
+} // extern "C"
+
#endif // TESTING_FUZZERS_PDFIUM_FUZZER_UTIL_H_
diff --git a/testing/fuzzers/pdfium_xfa_fuzzer.cc b/testing/fuzzers/pdfium_xfa_fuzzer.cc
index f9a69d4..1ba78cc 100644
--- a/testing/fuzzers/pdfium_xfa_fuzzer.cc
+++ b/testing/fuzzers/pdfium_xfa_fuzzer.cc
@@ -1,4 +1,4 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
diff --git a/testing/fuzzers/pdfium_xfa_lpm_fuzz_stub.cc b/testing/fuzzers/pdfium_xfa_lpm_fuzz_stub.cc
index c4b55b6..9f6c78a 100644
--- a/testing/fuzzers/pdfium_xfa_lpm_fuzz_stub.cc
+++ b/testing/fuzzers/pdfium_xfa_lpm_fuzz_stub.cc
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
diff --git a/testing/fuzzers/pdfium_xfa_lpm_fuzz_stub.h b/testing/fuzzers/pdfium_xfa_lpm_fuzz_stub.h
index bb6bf1d..a7c2ec0 100644
--- a/testing/fuzzers/pdfium_xfa_lpm_fuzz_stub.h
+++ b/testing/fuzzers/pdfium_xfa_lpm_fuzz_stub.h
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
diff --git a/testing/fuzzers/xfa_codec_fuzzer.h b/testing/fuzzers/xfa_codec_fuzzer.h
index 4f5668d..b14b7d7 100644
--- a/testing/fuzzers/xfa_codec_fuzzer.h
+++ b/testing/fuzzers/xfa_codec_fuzzer.h
@@ -1,4 +1,4 @@
-// Copyright 2016 The PDFium Authors. All rights reserved.
+// Copyright 2016 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -6,12 +6,14 @@
#define TESTING_FUZZERS_XFA_CODEC_FUZZER_H_
#include <memory>
+#include <utility>
#include "core/fxcodec/fx_codec.h"
-#include "core/fxcodec/progressivedecoder.h"
-#include "core/fxcrt/cfx_readonlymemorystream.h"
+#include "core/fxcodec/progressive_decoder.h"
+#include "core/fxcrt/cfx_read_only_span_stream.h"
+#include "core/fxcrt/fx_safe_types.h"
+#include "core/fxcrt/retain_ptr.h"
#include "core/fxge/dib/cfx_dibitmap.h"
-#include "third_party/base/ptr_util.h"
#include "third_party/base/span.h"
// Support up to 64 MB. This prevents trivial OOM when MSAN is on and
@@ -21,14 +23,13 @@
class XFACodecFuzzer {
public:
static int Fuzz(const uint8_t* data, size_t size, FXCODEC_IMAGE_TYPE type) {
- auto* mgr = fxcodec::ModuleMgr::GetInstance();
- std::unique_ptr<ProgressiveDecoder> decoder =
- mgr->CreateProgressiveDecoder();
- auto source = pdfium::MakeRetain<CFX_ReadOnlyMemoryStream>(
+ auto decoder = std::make_unique<ProgressiveDecoder>();
+ auto source = pdfium::MakeRetain<CFX_ReadOnlySpanStream>(
pdfium::make_span(data, size));
CFX_DIBAttribute attr;
- FXCODEC_STATUS status = decoder->LoadImageInfo(source, type, &attr, true);
- if (status != FXCODEC_STATUS_FRAME_READY)
+ FXCODEC_STATUS status =
+ decoder->LoadImageInfo(std::move(source), type, &attr, true);
+ if (status != FXCODEC_STATUS::kFrameReady)
return 0;
// Skipping very large images, since they will take a long time and may lead
@@ -42,16 +43,17 @@
}
auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
- bitmap->Create(decoder->GetWidth(), decoder->GetHeight(), FXDIB_Argb);
+ bitmap->Create(decoder->GetWidth(), decoder->GetHeight(),
+ FXDIB_Format::kArgb);
size_t frames;
std::tie(status, frames) = decoder->GetFrames();
- if (status != FXCODEC_STATUS_DECODE_READY || frames == 0)
+ if (status != FXCODEC_STATUS::kDecodeReady || frames == 0)
return 0;
status = decoder->StartDecode(bitmap, 0, 0, bitmap->GetWidth(),
bitmap->GetHeight());
- while (status == FXCODEC_STATUS_DECODE_TOBECONTINUE)
+ while (status == FXCODEC_STATUS::kDecodeToBeContinued)
status = decoder->ContinueDecode();
return 0;
diff --git a/testing/fuzzers/xfa_process_state.cc b/testing/fuzzers/xfa_process_state.cc
new file mode 100644
index 0000000..f768fea
--- /dev/null
+++ b/testing/fuzzers/xfa_process_state.cc
@@ -0,0 +1,25 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/fuzzers/xfa_process_state.h"
+
+#include "fxjs/gc/heap.h"
+#include "v8/include/libplatform/libplatform.h"
+
+XFAProcessState::XFAProcessState(v8::Platform* platform, v8::Isolate* isolate)
+ : platform_(platform), isolate_(isolate), heap_(FXGC_CreateHeap()) {}
+
+XFAProcessState::~XFAProcessState() {
+ FXGC_ForceGarbageCollection(heap_.get());
+}
+
+cppgc::Heap* XFAProcessState::GetHeap() const {
+ return heap_.get();
+}
+
+void XFAProcessState::ForceGCAndPump() {
+ FXGC_ForceGarbageCollection(heap_.get());
+ while (v8::platform::PumpMessageLoop(platform_, isolate_))
+ continue;
+}
diff --git a/testing/fuzzers/xfa_process_state.h b/testing/fuzzers/xfa_process_state.h
new file mode 100644
index 0000000..12d8a7d
--- /dev/null
+++ b/testing/fuzzers/xfa_process_state.h
@@ -0,0 +1,33 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TESTING_FUZZERS_XFA_PROCESS_STATE_H_
+#define TESTING_FUZZERS_XFA_PROCESS_STATE_H_
+
+#if !defined(PDF_ENABLE_XFA)
+#error "XFA only"
+#endif
+
+#include "fxjs/gc/heap.h"
+
+namespace v8 {
+class Isolate;
+class Platform;
+} // namespace v8
+
+class XFAProcessState {
+ public:
+ XFAProcessState(v8::Platform* platform, v8::Isolate* isolate);
+ ~XFAProcessState();
+
+ cppgc::Heap* GetHeap() const;
+ void ForceGCAndPump();
+
+ private:
+ v8::Platform* const platform_;
+ v8::Isolate* const isolate_;
+ FXGCScopedHeap heap_;
+};
+
+#endif // TESTING_FUZZERS_XFA_PROCESS_STATE_H_
diff --git a/testing/fx_string_testhelpers.cpp b/testing/fx_string_testhelpers.cpp
index 4a7bda7..8e76a76 100644
--- a/testing/fx_string_testhelpers.cpp
+++ b/testing/fx_string_testhelpers.cpp
@@ -1,4 +1,4 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -6,8 +6,11 @@
#include <iomanip>
#include <ios>
+#include <ostream>
+#include "core/fxcrt/cfx_datetime.h"
#include "core/fxcrt/fx_string.h"
+#include "third_party/base/check_op.h"
#include "third_party/base/span.h"
std::ostream& operator<<(std::ostream& os, const CFX_DateTime& dt) {
@@ -22,7 +25,7 @@
std::vector<std::string> StringSplit(const std::string& str, char delimiter) {
std::vector<std::string> result;
size_t pos = 0;
- while (1) {
+ while (true) {
size_t found = str.find(delimiter, pos);
if (found == std::string::npos)
break;
@@ -48,16 +51,17 @@
while (wstr[characters])
++characters;
- std::wstring platform_string(characters, L'\0');
- for (size_t i = 0; i < characters + 1; ++i) {
+ std::wstring platform_string;
+ platform_string.reserve(characters);
+ for (size_t i = 0; i < characters; ++i) {
const unsigned char* ptr = reinterpret_cast<const unsigned char*>(&wstr[i]);
- platform_string[i] = ptr[0] + 256 * ptr[1];
+ platform_string.push_back(ptr[0] + 256 * ptr[1]);
}
return platform_string;
}
ScopedFPDFWideString GetFPDFWideString(const std::wstring& wstr) {
- size_t length = sizeof(uint16_t) * (wstr.length() + 1);
+ size_t length = sizeof(uint16_t) * (wstr.size() + 1);
ScopedFPDFWideString result(static_cast<FPDF_WCHAR*>(malloc(length)));
pdfium::span<uint8_t> result_span(reinterpret_cast<uint8_t*>(result.get()),
length);
@@ -72,6 +76,6 @@
}
std::vector<FPDF_WCHAR> GetFPDFWideStringBuffer(size_t length_bytes) {
- ASSERT(length_bytes % sizeof(FPDF_WCHAR) == 0);
+ DCHECK_EQ(length_bytes % sizeof(FPDF_WCHAR), 0);
return std::vector<FPDF_WCHAR>(length_bytes / sizeof(FPDF_WCHAR));
}
diff --git a/testing/fx_string_testhelpers.h b/testing/fx_string_testhelpers.h
index b90838a..207947c 100644
--- a/testing/fx_string_testhelpers.h
+++ b/testing/fx_string_testhelpers.h
@@ -1,19 +1,20 @@
-// Copyright 2014 PDFium Authors. All rights reserved.
+// Copyright 2014 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef TESTING_FX_STRING_TESTHELPERS_H_
#define TESTING_FX_STRING_TESTHELPERS_H_
+#include <iosfwd>
#include <memory>
-#include <ostream>
#include <string>
#include <vector>
-#include "core/fxcrt/cfx_datetime.h"
#include "public/fpdfview.h"
#include "testing/free_deleter.h"
+class CFX_DateTime;
+
// Output stream operator so GTEST macros work with CFX_DateTime objects.
std::ostream& operator<<(std::ostream& os, const CFX_DateTime& dt);
diff --git a/testing/fxgc_unittest.cpp b/testing/fxgc_unittest.cpp
new file mode 100644
index 0000000..cc01ab4
--- /dev/null
+++ b/testing/fxgc_unittest.cpp
@@ -0,0 +1,39 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/fxgc_unittest.h"
+
+#include "fxjs/gc/heap.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/v8_test_environment.h"
+#include "v8/include/libplatform/libplatform.h"
+
+FXGCUnitTest::FXGCUnitTest() = default;
+
+FXGCUnitTest::~FXGCUnitTest() = default;
+
+void FXGCUnitTest::SetUp() {
+ ::testing::Test::SetUp();
+ auto* env = V8TestEnvironment::GetInstance();
+ FXGC_Initialize(env->platform(), env->isolate());
+ heap_ = FXGC_CreateHeap();
+ ASSERT_TRUE(heap_);
+}
+
+void FXGCUnitTest::TearDown() {
+ ForceGCAndPump();
+ heap_.reset();
+ FXGC_Release();
+ ::testing::Test::TearDown();
+}
+
+void FXGCUnitTest::ForceGCAndPump() {
+ FXGC_ForceGarbageCollection(heap_.get());
+ Pump();
+}
+
+void FXGCUnitTest::Pump() {
+ V8TestEnvironment::PumpPlatformMessageLoop(
+ V8TestEnvironment::GetInstance()->isolate());
+}
diff --git a/testing/fxgc_unittest.h b/testing/fxgc_unittest.h
new file mode 100644
index 0000000..0625190
--- /dev/null
+++ b/testing/fxgc_unittest.h
@@ -0,0 +1,28 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TESTING_FXGC_UNITTEST_H_
+#define TESTING_FXGC_UNITTEST_H_
+
+#include "fxjs/gc/heap.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class FXGCUnitTest : public ::testing::Test {
+ public:
+ FXGCUnitTest();
+ ~FXGCUnitTest() override;
+
+ // testing::Test:
+ void SetUp() override;
+ void TearDown() override;
+
+ cppgc::Heap* heap() const { return heap_.get(); }
+ void ForceGCAndPump();
+ void Pump();
+
+ private:
+ FXGCScopedHeap heap_;
+};
+
+#endif // TESTING_FXGC_UNITTEST_H_
diff --git a/testing/fxv8_unittest.cpp b/testing/fxv8_unittest.cpp
new file mode 100644
index 0000000..fdd4c08
--- /dev/null
+++ b/testing/fxv8_unittest.cpp
@@ -0,0 +1,26 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/fxv8_unittest.h"
+
+#include <memory>
+
+#include "fxjs/cfx_v8_array_buffer_allocator.h"
+#include "v8/include/v8-isolate.h"
+
+void FXV8UnitTest::V8IsolateDeleter::operator()(v8::Isolate* ptr) const {
+ ptr->Dispose();
+}
+
+FXV8UnitTest::FXV8UnitTest() = default;
+
+FXV8UnitTest::~FXV8UnitTest() = default;
+
+void FXV8UnitTest::SetUp() {
+ array_buffer_allocator_ = std::make_unique<CFX_V8ArrayBufferAllocator>();
+
+ v8::Isolate::CreateParams params;
+ params.array_buffer_allocator = array_buffer_allocator_.get();
+ isolate_.reset(v8::Isolate::New(params));
+}
diff --git a/testing/fxv8_unittest.h b/testing/fxv8_unittest.h
new file mode 100644
index 0000000..0757c73
--- /dev/null
+++ b/testing/fxv8_unittest.h
@@ -0,0 +1,37 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TESTING_FXV8_UNITTEST_H_
+#define TESTING_FXV8_UNITTEST_H_
+
+#include <memory>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+class CFX_V8;
+class CFX_V8ArrayBufferAllocator;
+
+namespace v8 {
+class Isolate;
+} // namespace v8
+
+class FXV8UnitTest : public ::testing::Test {
+ public:
+ struct V8IsolateDeleter {
+ void operator()(v8::Isolate* ptr) const;
+ };
+
+ FXV8UnitTest();
+ ~FXV8UnitTest() override;
+
+ void SetUp() override;
+
+ v8::Isolate* isolate() const { return isolate_.get(); }
+
+ protected:
+ std::unique_ptr<CFX_V8ArrayBufferAllocator> array_buffer_allocator_;
+ std::unique_ptr<v8::Isolate, V8IsolateDeleter> isolate_;
+};
+
+#endif // TESTING_FXV8_UNITTEST_H_
diff --git a/testing/gmock/BUILD.gn b/testing/gmock/BUILD.gn
index d7de2e9..feb8240 100644
--- a/testing/gmock/BUILD.gn
+++ b/testing/gmock/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2018 The PDFium Authors. All rights reserved.
+# Copyright 2018 The PDFium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -6,31 +6,14 @@
# it stabilizes, Chromium code MUST use this target instead of reaching directly
# into //third_party/googletest.
-import("//build_overrides/build.gni")
-
source_set("gmock") {
testonly = true
sources = [
"include/gmock/gmock-actions.h",
- "include/gmock/gmock-generated-function-mockers.h",
"include/gmock/gmock-matchers.h",
"include/gmock/gmock.h",
]
- deps = [ "//third_party/googletest:gmock" ]
-
- # TODO(crbug.com/806952): Depending on gmock_mutant only if build_with_chromium,
- # because gmock_mutant depends on //base which uses C++14. Since gmock is a
- # third_party library used by other projects it should not include C++14 only code.
- if (build_with_chromium) {
- # Allow Chromium targets depending on gmock to #include testing/gmock_mutant.h
- # without triggering a `gn check` error.
- public_deps = [ "//testing:gmock_mutant" ]
- }
-
- public_configs = [
- "//third_party/googletest:gmock_config",
- "//third_party/googletest:gtest_config",
- ]
+ public_deps = [ "//third_party/googletest:gmock" ]
}
# The file/directory layout of Google Test is not yet considered stable. Until
diff --git a/testing/gmock/include/gmock/gmock-actions.h b/testing/gmock/include/gmock/gmock-actions.h
index fb193e5..21fc6b0 100644
--- a/testing/gmock/include/gmock/gmock-actions.h
+++ b/testing/gmock/include/gmock/gmock-actions.h
@@ -1,4 +1,4 @@
-// Copyright 2018 The PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
diff --git a/testing/gmock/include/gmock/gmock-generated-function-mockers.h b/testing/gmock/include/gmock/gmock-generated-function-mockers.h
index 57cbc0a..8ce51a0 100644
--- a/testing/gmock/include/gmock/gmock-generated-function-mockers.h
+++ b/testing/gmock/include/gmock/gmock-generated-function-mockers.h
@@ -1,4 +1,4 @@
-// Copyright 2018 The PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
diff --git a/testing/gmock/include/gmock/gmock-matchers.h b/testing/gmock/include/gmock/gmock-matchers.h
index 25d25e9..ca7cf0e 100644
--- a/testing/gmock/include/gmock/gmock-matchers.h
+++ b/testing/gmock/include/gmock/gmock-matchers.h
@@ -1,4 +1,4 @@
-// Copyright 2018 The PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
diff --git a/testing/gmock/include/gmock/gmock.h b/testing/gmock/include/gmock/gmock.h
index a344bcb..dfcd2d4 100644
--- a/testing/gmock/include/gmock/gmock.h
+++ b/testing/gmock/include/gmock/gmock.h
@@ -1,4 +1,4 @@
-// Copyright 2018 The PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
diff --git a/testing/gtest/BUILD.gn b/testing/gtest/BUILD.gn
index ad0b269..f6b98df 100644
--- a/testing/gtest/BUILD.gn
+++ b/testing/gtest/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2018 The PDFium Authors. All rights reserved.
+# Copyright 2018 The PDFium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -55,10 +55,7 @@
sources += [ "../platform_test.h" ]
}
- if ((is_mac || is_ios) && gtest_include_objc_support) {
- if (is_ios) {
- set_sources_assignment_filter([])
- }
+ if (is_apple && gtest_include_objc_support) {
sources += [
"../gtest_mac.h",
"../gtest_mac.mm",
@@ -66,15 +63,10 @@
if (gtest_include_platform_test) {
sources += [ "../platform_test_mac.mm" ]
}
- set_sources_assignment_filter(sources_assignment_filter)
}
if (is_ios && gtest_include_ios_coverage) {
- sources += [
- "../coverage_util_ios.h",
- "../coverage_util_ios.mm",
- ]
- deps = [ ":ios_enable_coverage" ]
+ public_deps += [ ":ios_coverage_utils" ]
}
}
@@ -87,6 +79,16 @@
}
if (is_ios) {
+ # These headers are needed in some non test targets for iOS code coverage. So
+ # can not be testonly.
+ source_set("ios_coverage_utils") {
+ sources = [
+ "../coverage_util_ios.h",
+ "../coverage_util_ios.mm",
+ ]
+ deps = [ ":ios_enable_coverage" ]
+ }
+
buildflag_header("ios_enable_coverage") {
header = "ios_enable_coverage.h"
flags = [ "IOS_ENABLE_COVERAGE=$use_clang_coverage" ]
diff --git a/testing/gtest/empty.cc b/testing/gtest/empty.cc
index 5186b59..7cfe79f 100644
--- a/testing/gtest/empty.cc
+++ b/testing/gtest/empty.cc
@@ -1,3 +1,3 @@
-// Copyright 2018 The PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
diff --git a/testing/gtest/include/gtest/gtest-death-test.h b/testing/gtest/include/gtest/gtest-death-test.h
new file mode 100644
index 0000000..6e79c82
--- /dev/null
+++ b/testing/gtest/include/gtest/gtest-death-test.h
@@ -0,0 +1,10 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// The file/directory layout of Google Test is not yet considered stable. Until
+// it stabilizes, PDFium code will use forwarding headers in testing/gtest
+// and testing/gmock, instead of directly including files in
+// third_party/googletest.
+
+#include "third_party/googletest/src/googletest/include/gtest/gtest-death-test.h"
diff --git a/testing/gtest/include/gtest/gtest-message.h b/testing/gtest/include/gtest/gtest-message.h
new file mode 100644
index 0000000..7859730
--- /dev/null
+++ b/testing/gtest/include/gtest/gtest-message.h
@@ -0,0 +1,10 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// The file/directory layout of Google Test is not yet considered stable. Until
+// it stabilizes, PDFium code will use forwarding headers in testing/gtest
+// and testing/gmock, instead of directly including files in
+// third_party/googletest.
+
+#include "third_party/googletest/src/googletest/include/gtest/gtest-message.h"
diff --git a/testing/gtest/include/gtest/gtest-param-test.h b/testing/gtest/include/gtest/gtest-param-test.h
new file mode 100644
index 0000000..3d681e4
--- /dev/null
+++ b/testing/gtest/include/gtest/gtest-param-test.h
@@ -0,0 +1,10 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// The file/directory layout of Google Test is not yet considered stable. Until
+// it stabilizes, PDFium code will use forwarding headers in testing/gtest
+// and testing/gmock, instead of directly including files in
+// third_party/googletest.
+
+#include "third_party/googletest/src/googletest/include/gtest/gtest-param-test.h"
diff --git a/testing/gtest/include/gtest/gtest-spi.h b/testing/gtest/include/gtest/gtest-spi.h
new file mode 100644
index 0000000..24ba5ee
--- /dev/null
+++ b/testing/gtest/include/gtest/gtest-spi.h
@@ -0,0 +1,10 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// The file/directory layout of Google Test is not yet considered stable. Until
+// it stabilizes, PDFium code will use forwarding headers in testing/gtest
+// and testing/gmock, instead of directly including files in
+// third_party/googletest.
+
+#include "third_party/googletest/src/googletest/include/gtest/gtest-spi.h"
diff --git a/testing/gtest/include/gtest/gtest.h b/testing/gtest/include/gtest/gtest.h
index 9425b25..4706280 100644
--- a/testing/gtest/include/gtest/gtest.h
+++ b/testing/gtest/include/gtest/gtest.h
@@ -1,9 +1,9 @@
-// Copyright 2018 The PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// The file/directory layout of Google Test is not yet considered stable. Until
-// it stabilizes, Chromium code will use forwarding headers in testing/gtest
+// it stabilizes, PDFium code will use forwarding headers in testing/gtest
// and testing/gmock, instead of directly including files in
// third_party/googletest.
diff --git a/testing/gtest/include/gtest/gtest_prod.h b/testing/gtest/include/gtest/gtest_prod.h
index 2d67b42..703a176 100644
--- a/testing/gtest/include/gtest/gtest_prod.h
+++ b/testing/gtest/include/gtest/gtest_prod.h
@@ -1,9 +1,9 @@
-// Copyright 2018 The PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// The file/directory layout of Google Test is not yet considered stable. Until
-// it stabilizes, Chromium code will use forwarding headers in testing/gtest
+// it stabilizes, PDFium code will use forwarding headers in testing/gtest
// and testing/gmock, instead of directly including files in
// third_party/googletest.
diff --git a/testing/gtest_mac.h b/testing/gtest_mac.h
index 0c0b655..95d7d32 100644
--- a/testing/gtest_mac.h
+++ b/testing/gtest_mac.h
@@ -1,6 +1,7 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright 2010 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+
#ifndef TESTING_GTEST_MAC_H_
#define TESTING_GTEST_MAC_H_
#include <gtest/internal/gtest-port.h>
diff --git a/testing/gtest_mac.mm b/testing/gtest_mac.mm
index b490f55..47912c7 100644
--- a/testing/gtest_mac.mm
+++ b/testing/gtest_mac.mm
@@ -1,6 +1,7 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright 2010 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+
#import "gtest_mac.h"
#include <string>
#include <gtest/gtest.h>
diff --git a/testing/image_diff/BUILD.gn b/testing/image_diff/BUILD.gn
index 141769b..0f9b71b 100644
--- a/testing/image_diff/BUILD.gn
+++ b/testing/image_diff/BUILD.gn
@@ -1,4 +1,4 @@
-# Copyright 2018 The PDFium Authors. All rights reserved.
+# Copyright 2018 The PDFium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -10,7 +10,10 @@
"image_diff_png.cpp",
"image_diff_png.h",
]
- configs += [ "../../:pdfium_core_config" ]
+ configs += [
+ "../../:pdfium_strict_config",
+ "../../:pdfium_noshorten_config",
+ ]
deps = [
"../../third_party:pdfium_base",
"../../third_party:png",
diff --git a/testing/image_diff/DEPS b/testing/image_diff/DEPS
index 4bd2335..fcac201 100644
--- a/testing/image_diff/DEPS
+++ b/testing/image_diff/DEPS
@@ -1,5 +1,5 @@
include_rules = [
- '+third_party/libpng16',
+ '+third_party/libpng',
'+third_party/zlib',
]
diff --git a/testing/image_diff/image_diff.cpp b/testing/image_diff/image_diff.cpp
index c8f9caf..a701921 100644
--- a/testing/image_diff/image_diff.cpp
+++ b/testing/image_diff/image_diff.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 2011 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -8,12 +8,12 @@
// The exact format of this tool's output to stdout is important, to match
// what the run-webkit-tests script expects.
-#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <algorithm>
+#include <cmath>
#include <map>
#include <string>
#include <vector>
@@ -21,10 +21,10 @@
#include "core/fxcrt/fx_memory.h"
#include "testing/image_diff/image_diff_png.h"
#include "testing/utils/path_service.h"
-#include "third_party/base/logging.h"
+#include "third_party/base/cxx17_backports.h"
#include "third_party/base/numerics/safe_conversions.h"
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
#include <windows.h>
#endif
@@ -39,8 +39,9 @@
class Image {
public:
- Image() : w_(0), h_(0) {}
- Image(const Image& image) : w_(image.w_), h_(image.h_), data_(image.data_) {}
+ Image() = default;
+ Image(const Image& image) = default;
+ Image& operator=(const Image& other) = default;
bool has_image() const { return w_ > 0 && h_ > 0; }
int w() const { return w_; }
@@ -110,8 +111,8 @@
size_t pixel_address(int x, int y) const { return (y * w_ + x) * 4; }
// Pixel dimensions of the image.
- int w_;
- int h_;
+ int w_ = 0;
+ int h_ = 0;
std::vector<uint8_t> data_;
};
@@ -143,7 +144,36 @@
*pixels_different += (max_h - h) * max_w;
}
-float PercentageDifferent(const Image& baseline, const Image& actual) {
+struct UnpackedPixel {
+ explicit UnpackedPixel(uint32_t packed)
+ : red(packed & 0xff),
+ green((packed >> 8) & 0xff),
+ blue((packed >> 16) & 0xff),
+ alpha((packed >> 24) & 0xff) {}
+
+ uint8_t red;
+ uint8_t green;
+ uint8_t blue;
+ uint8_t alpha;
+};
+
+uint8_t ChannelDelta(uint8_t baseline_channel, uint8_t actual_channel) {
+ // No casts are necessary because arithmetic operators implicitly convert
+ // `uint8_t` to `int` first. The final delta is always in the range 0 to 255.
+ return std::abs(baseline_channel - actual_channel);
+}
+
+uint8_t MaxPixelPerChannelDelta(const UnpackedPixel& baseline_pixel,
+ const UnpackedPixel& actual_pixel) {
+ return std::max({ChannelDelta(baseline_pixel.red, actual_pixel.red),
+ ChannelDelta(baseline_pixel.green, actual_pixel.green),
+ ChannelDelta(baseline_pixel.blue, actual_pixel.blue),
+ ChannelDelta(baseline_pixel.alpha, actual_pixel.alpha)});
+}
+
+float PercentageDifferent(const Image& baseline,
+ const Image& actual,
+ uint8_t max_pixel_per_channel_delta) {
int w = std::min(baseline.w(), actual.w());
int h = std::min(baseline.h(), actual.h());
@@ -151,8 +181,17 @@
int pixels_different = 0;
for (int y = 0; y < h; ++y) {
for (int x = 0; x < w; ++x) {
- if (baseline.pixel_at(x, y) != actual.pixel_at(x, y))
+ const uint32_t baseline_pixel = baseline.pixel_at(x, y);
+ const uint32_t actual_pixel = actual.pixel_at(x, y);
+ if (baseline_pixel == actual_pixel) {
+ continue;
+ }
+
+ if (MaxPixelPerChannelDelta(UnpackedPixel(baseline_pixel),
+ UnpackedPixel(actual_pixel)) >
+ max_pixel_per_channel_delta) {
++pixels_different;
+ }
}
}
@@ -198,23 +237,31 @@
fprintf(
stderr,
"Usage:\n"
- " %s OPTIONS <compare file> <reference file>\n"
- " Compares two files on disk, returning 0 when they are the same;\n"
+ " %s OPTIONS <compare_file> <reference_file>\n"
+ " Compares two files on disk, returning 0 when they are the same.\n"
" Passing \"--histogram\" additionally calculates a diff of the\n"
- " RGBA value histograms. (which is resistant to shifts in layout)\n"
- " Passing \"--reverse-byte-order\" additionally assumes the compare\n"
- " file has BGRA byte ordering.\n"
- " %s --diff <compare file> <reference file> <output file>\n"
- " Compares two files on disk, outputs an image that visualizes the\n"
- " difference to <output file>\n",
- binary_name.c_str(), binary_name.c_str());
+ " RGBA value histograms (which is resistant to shifts in layout).\n"
+ " Passing \"--reverse-byte-order\" additionally assumes the\n"
+ " compare file has BGRA byte ordering.\n"
+ " Passing \"--fuzzy\" additionally allows individual pixels to\n"
+ " differ by at most 1 on each channel.\n\n"
+ " %s --diff <compare_file> <reference_file> <output_file>\n"
+ " Compares two files on disk, and if they differ, outputs an image\n"
+ " to <output_file> that visualizes the differing pixels as red\n"
+ " dots.\n\n"
+ " %s --subtract <compare_file> <reference_file> <output_file>\n"
+ " Compares two files on disk, and if they differ, outputs an image\n"
+ " to <output_file> that visualizes the difference as a scaled\n"
+ " subtraction of pixel values.\n",
+ binary_name.c_str(), binary_name.c_str(), binary_name.c_str());
}
int CompareImages(const std::string& binary_name,
const std::string& file1,
const std::string& file2,
bool compare_histograms,
- bool reverse_byte_order) {
+ bool reverse_byte_order,
+ uint8_t max_pixel_per_channel_delta) {
Image actual_image;
Image baseline_image;
@@ -240,7 +287,8 @@
}
const char* const diff_name = compare_histograms ? "exact diff" : "diff";
- float percent = PercentageDifferent(actual_image, baseline_image);
+ float percent = PercentageDifferent(actual_image, baseline_image,
+ max_pixel_per_channel_delta);
const char* const passed = percent > 0.0 ? "failed" : "passed";
printf("%s: %01.2f%% %s\n", diff_name, percent, passed);
@@ -280,10 +328,48 @@
return same;
}
+bool SubtractImages(const Image& image1, const Image& image2, Image* out) {
+ int w = std::min(image1.w(), image2.w());
+ int h = std::min(image1.h(), image2.h());
+ *out = Image(image1);
+ bool same = (image1.w() == image2.w()) && (image1.h() == image2.h());
+
+ for (int y = 0; y < h; ++y) {
+ for (int x = 0; x < w; ++x) {
+ uint32_t pixel1 = image1.pixel_at(x, y);
+ int32_t r1 = pixel1 & 0xff;
+ int32_t g1 = (pixel1 >> 8) & 0xff;
+ int32_t b1 = (pixel1 >> 16) & 0xff;
+
+ uint32_t pixel2 = image2.pixel_at(x, y);
+ int32_t r2 = pixel2 & 0xff;
+ int32_t g2 = (pixel2 >> 8) & 0xff;
+ int32_t b2 = (pixel2 >> 16) & 0xff;
+
+ int32_t delta_r = r1 - r2;
+ int32_t delta_g = g1 - g2;
+ int32_t delta_b = b1 - b2;
+ same &= (delta_r == 0 && delta_g == 0 && delta_b == 0);
+
+ delta_r = pdfium::clamp(128 + delta_r * 8, 0, 255);
+ delta_g = pdfium::clamp(128 + delta_g * 8, 0, 255);
+ delta_b = pdfium::clamp(128 + delta_b * 8, 0, 255);
+
+ uint32_t new_pixel = RGBA_ALPHA;
+ new_pixel |= delta_r;
+ new_pixel |= (delta_g << 8);
+ new_pixel |= (delta_b << 16);
+ out->set_pixel_at(x, y, new_pixel);
+ }
+ }
+ return same;
+}
+
int DiffImages(const std::string& binary_name,
const std::string& file1,
const std::string& file2,
- const std::string& out_file) {
+ const std::string& out_file,
+ bool do_subtraction) {
Image actual_image;
Image baseline_image;
@@ -299,7 +385,9 @@
}
Image diff_image;
- bool same = CreateImageDiff(baseline_image, actual_image, &diff_image);
+ bool same = do_subtraction
+ ? SubtractImages(baseline_image, actual_image, &diff_image)
+ : CreateImageDiff(baseline_image, actual_image, &diff_image);
if (same)
return kStatusSame;
@@ -321,11 +409,13 @@
}
int main(int argc, const char* argv[]) {
- FXMEM_InitializePartitionAlloc();
+ FX_InitializeMemoryAllocators();
bool histograms = false;
bool produce_diff_image = false;
+ bool produce_image_subtraction = false;
bool reverse_byte_order = false;
+ uint8_t max_pixel_per_channel_delta = 0;
std::string filename1;
std::string filename2;
std::string diff_filename;
@@ -343,8 +433,12 @@
histograms = true;
} else if (strcmp(arg, "--diff") == 0) {
produce_diff_image = true;
+ } else if (strcmp(arg, "--subtract") == 0) {
+ produce_image_subtraction = true;
} else if (strcmp(arg, "--reverse-byte-order") == 0) {
reverse_byte_order = true;
+ } else if (strcmp(arg, "--fuzzy") == 0) {
+ max_pixel_per_channel_delta = 1;
}
}
if (i < argc)
@@ -354,13 +448,14 @@
if (i < argc)
diff_filename = argv[i++];
- if (produce_diff_image) {
+ if (produce_diff_image || produce_image_subtraction) {
if (!diff_filename.empty()) {
- return DiffImages(binary_name, filename1, filename2, diff_filename);
+ return DiffImages(binary_name, filename1, filename2, diff_filename,
+ produce_image_subtraction);
}
} else if (!filename2.empty()) {
return CompareImages(binary_name, filename1, filename2, histograms,
- reverse_byte_order);
+ reverse_byte_order, max_pixel_per_channel_delta);
}
PrintHelp(binary_name);
diff --git a/testing/image_diff/image_diff_png.cpp b/testing/image_diff/image_diff_png.cpp
index dcd9d69..c2e8b03 100644
--- a/testing/image_diff/image_diff_png.cpp
+++ b/testing/image_diff/image_diff_png.cpp
@@ -1,4 +1,4 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// Copyright 2013 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -16,8 +16,7 @@
#include <string>
-#include "third_party/base/compiler_specific.h"
-#include "third_party/base/logging.h"
+#include "third_party/base/notreached.h"
#ifdef USE_SYSTEM_ZLIB
#include <zlib.h>
@@ -28,7 +27,7 @@
#ifdef USE_SYSTEM_LIBPNG
#include <png.h>
#else
-#include "third_party/libpng16/png.h"
+#include "third_party/libpng/png.h"
#endif
namespace image_diff_png {
@@ -92,12 +91,12 @@
int pixel_width,
uint8_t* rgb,
bool* is_opaque) {
+ const uint8_t* pixel_in = rgba;
+ uint8_t* pixel_out = rgb;
for (int x = 0; x < pixel_width; x++) {
- const uint8_t* pixel_in = &rgba[x * 4];
- uint8_t* pixel_out = &rgb[x * 3];
- pixel_out[0] = pixel_in[0];
- pixel_out[1] = pixel_in[1];
- pixel_out[2] = pixel_in[2];
+ memcpy(pixel_out, pixel_in, 3);
+ pixel_in += 4;
+ pixel_out += 3;
}
}
@@ -148,13 +147,13 @@
int pixel_width,
uint8_t* rgba,
bool* is_opaque) {
+ const uint8_t* pixel_in = rgb;
+ uint8_t* pixel_out = rgba;
for (int x = 0; x < pixel_width; x++) {
- const uint8_t* pixel_in = &rgb[x * 3];
- uint8_t* pixel_out = &rgba[x * 4];
- pixel_out[0] = pixel_in[0];
- pixel_out[1] = pixel_in[1];
- pixel_out[2] = pixel_in[2];
+ memcpy(pixel_out, pixel_in, 3);
pixel_out[3] = 0xff;
+ pixel_in += 3;
+ pixel_out += 4;
}
}
@@ -428,7 +427,7 @@
#ifdef PNG_TEXT_SUPPORTED
inline char* strdup(const char* str) {
-#if defined(OS_WIN)
+#if BUILDFLAG(IS_WIN)
return _strdup(str);
#else
return ::strdup(str);
@@ -461,11 +460,11 @@
void AddComment(size_t pos, const Comment& comment) {
png_text_[pos].compression = PNG_TEXT_COMPRESSION_NONE;
// A PNG comment's key can only be 79 characters long.
- if (comment.key.length() > 79)
+ if (comment.key.size() > 79)
return;
png_text_[pos].key = strdup(comment.key.substr(0, 78).c_str());
png_text_[pos].text = strdup(comment.text.c_str());
- png_text_[pos].text_length = comment.text.length();
+ png_text_[pos].text_length = comment.text.size();
#ifdef PNG_iTXt_SUPPORTED
png_text_[pos].itxt_length = 0;
png_text_[pos].lang = 0;
@@ -571,7 +570,7 @@
switch (format) {
case FORMAT_BGR:
converter = ConvertBGRtoRGB;
- FALLTHROUGH;
+ [[fallthrough]];
case FORMAT_RGB:
input_color_components = 3;
diff --git a/testing/image_diff/image_diff_png.h b/testing/image_diff/image_diff_png.h
index 2c39bf0..370ce32 100644
--- a/testing/image_diff/image_diff_png.h
+++ b/testing/image_diff/image_diff_png.h
@@ -1,4 +1,4 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// Copyright 2013 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
diff --git a/testing/invalid_seekable_read_stream.cpp b/testing/invalid_seekable_read_stream.cpp
index a4116b0..778783c 100644
--- a/testing/invalid_seekable_read_stream.cpp
+++ b/testing/invalid_seekable_read_stream.cpp
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -9,9 +9,8 @@
InvalidSeekableReadStream::~InvalidSeekableReadStream() = default;
-bool InvalidSeekableReadStream::ReadBlockAtOffset(void* buffer,
- FX_FILESIZE offset,
- size_t size) {
+bool InvalidSeekableReadStream::ReadBlockAtOffset(pdfium::span<uint8_t> buffer,
+ FX_FILESIZE offset) {
return false;
}
diff --git a/testing/invalid_seekable_read_stream.h b/testing/invalid_seekable_read_stream.h
index 9322bc6..ae24b1a 100644
--- a/testing/invalid_seekable_read_stream.h
+++ b/testing/invalid_seekable_read_stream.h
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -6,17 +6,16 @@
#define TESTING_INVALID_SEEKABLE_READ_STREAM_H_
#include "core/fxcrt/fx_stream.h"
+#include "core/fxcrt/retain_ptr.h"
// A stream used for testing where reads always fail.
class InvalidSeekableReadStream final : public IFX_SeekableReadStream {
public:
- template <typename T, typename... Args>
- friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
+ CONSTRUCT_VIA_MAKE_RETAIN;
// IFX_SeekableReadStream overrides:
- bool ReadBlockAtOffset(void* buffer,
- FX_FILESIZE offset,
- size_t size) override;
+ bool ReadBlockAtOffset(pdfium::span<uint8_t> buffer,
+ FX_FILESIZE offset) override;
FX_FILESIZE GetSize() override;
private:
diff --git a/testing/js_embedder_test.cpp b/testing/js_embedder_test.cpp
index 57e6c4d..d6f800c 100644
--- a/testing/js_embedder_test.cpp
+++ b/testing/js_embedder_test.cpp
@@ -1,40 +1,15 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "testing/js_embedder_test.h"
-#include "fxjs/cfxjs_engine.h"
-#include "third_party/base/ptr_util.h"
+#include "testing/v8_test_environment.h"
-JSEmbedderTest::JSEmbedderTest()
- : m_pArrayBufferAllocator(
- pdfium::MakeUnique<CFX_V8ArrayBufferAllocator>()) {}
+JSEmbedderTest::JSEmbedderTest() = default;
-JSEmbedderTest::~JSEmbedderTest() {}
+JSEmbedderTest::~JSEmbedderTest() = default;
-void JSEmbedderTest::SetUp() {
- v8::Isolate::CreateParams params;
- params.array_buffer_allocator = m_pArrayBufferAllocator.get();
- m_pIsolate.reset(v8::Isolate::New(params));
-
- EmbedderTest::SetExternalIsolate(isolate());
- EmbedderTest::SetUp();
-
- v8::Isolate::Scope isolate_scope(isolate());
- v8::HandleScope handle_scope(isolate());
- FXJS_PerIsolateData::SetUp(isolate());
- m_Engine = pdfium::MakeUnique<CFXJS_Engine>(isolate());
- m_Engine->InitializeEngine();
-}
-
-void JSEmbedderTest::TearDown() {
- m_Engine->ReleaseEngine();
- m_Engine.reset();
- EmbedderTest::TearDown();
- m_pIsolate.reset();
-}
-
-v8::Local<v8::Context> JSEmbedderTest::GetV8Context() {
- return m_Engine->GetV8Context();
+v8::Isolate* JSEmbedderTest::isolate() const {
+ return V8TestEnvironment::GetInstance()->isolate();
}
diff --git a/testing/js_embedder_test.h b/testing/js_embedder_test.h
index dca0bd7..dda285a 100644
--- a/testing/js_embedder_test.h
+++ b/testing/js_embedder_test.h
@@ -1,35 +1,22 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef TESTING_JS_EMBEDDER_TEST_H_
#define TESTING_JS_EMBEDDER_TEST_H_
-#include <memory>
-
-#include "fxjs/cfx_v8.h"
#include "testing/embedder_test.h"
-#include "v8/include/v8.h"
-class CFXJS_Engine;
-class CFX_V8ArrayBufferAllocator;
+namespace v8 {
+class Isolate;
+} // namespace v8
class JSEmbedderTest : public EmbedderTest {
public:
JSEmbedderTest();
~JSEmbedderTest() override;
- void SetUp() override;
- void TearDown() override;
-
- v8::Isolate* isolate() const { return m_pIsolate.get(); }
- CFXJS_Engine* engine() const { return m_Engine.get(); }
- v8::Local<v8::Context> GetV8Context();
-
- private:
- std::unique_ptr<CFX_V8ArrayBufferAllocator> m_pArrayBufferAllocator;
- std::unique_ptr<v8::Isolate, CFX_V8IsolateDeleter> m_pIsolate;
- std::unique_ptr<CFXJS_Engine> m_Engine;
+ v8::Isolate* isolate() const;
};
#endif // TESTING_JS_EMBEDDER_TEST_H_
diff --git a/testing/pdf_test_environment.cpp b/testing/pdf_test_environment.cpp
new file mode 100644
index 0000000..2865027
--- /dev/null
+++ b/testing/pdf_test_environment.cpp
@@ -0,0 +1,20 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/pdf_test_environment.h"
+
+#include "core/fxge/cfx_gemodule.h"
+
+PDFTestEnvironment::PDFTestEnvironment() = default;
+
+PDFTestEnvironment::~PDFTestEnvironment() = default;
+
+// testing::Environment:
+void PDFTestEnvironment::SetUp() {
+ CFX_GEModule::Create(test_fonts_.font_paths());
+}
+
+void PDFTestEnvironment::TearDown() {
+ CFX_GEModule::Destroy();
+}
diff --git a/testing/pdf_test_environment.h b/testing/pdf_test_environment.h
new file mode 100644
index 0000000..818d8e4
--- /dev/null
+++ b/testing/pdf_test_environment.h
@@ -0,0 +1,24 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TESTING_PDF_TEST_ENVIRONMENT_H_
+#define TESTING_PDF_TEST_ENVIRONMENT_H_
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/test_fonts.h"
+
+class PDFTestEnvironment : public testing::Environment {
+ public:
+ PDFTestEnvironment();
+ ~PDFTestEnvironment() override;
+
+ // testing::Environment:
+ void SetUp() override;
+ void TearDown() override;
+
+ private:
+ TestFonts test_fonts_;
+};
+
+#endif // TESTING_PDF_TEST_ENVIRONMENT_H_
diff --git a/testing/pseudo_retainable.h b/testing/pseudo_retainable.h
index c4390d6..6dbfffc 100644
--- a/testing/pseudo_retainable.h
+++ b/testing/pseudo_retainable.h
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -8,8 +8,8 @@
class PseudoRetainable {
public:
PseudoRetainable() = default;
- void Retain() { ++retain_count_; }
- void Release() {
+ void Retain() const { ++retain_count_; }
+ void Release() const {
if (++release_count_ == retain_count_)
alive_ = false;
}
@@ -18,9 +18,9 @@
int release_count() const { return release_count_; }
private:
- bool alive_ = true;
- int retain_count_ = 0;
- int release_count_ = 0;
+ mutable bool alive_ = true;
+ mutable int retain_count_ = 0;
+ mutable int release_count_ = 0;
};
#endif // TESTING_PSEUDO_RETAINABLE_H_
diff --git a/testing/range_set.cpp b/testing/range_set.cpp
index 2fc540f..725fd7d 100644
--- a/testing/range_set.cpp
+++ b/testing/range_set.cpp
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -7,9 +7,11 @@
#include <algorithm>
#include "core/fxcrt/fx_system.h"
+#include "third_party/base/check.h"
-RangeSet::RangeSet() {}
-RangeSet::~RangeSet() {}
+RangeSet::RangeSet() = default;
+
+RangeSet::~RangeSet() = default;
bool RangeSet::Contains(const Range& range) const {
if (IsEmptyRange(range))
@@ -51,15 +53,14 @@
--end;
- const int new_start = std::min<size_t>(start->first, fixed_range.first);
- const int new_end = std::max(end->second, fixed_range.second);
-
+ const size_t new_start = std::min(start->first, fixed_range.first);
+ const size_t new_end = std::max(end->second, fixed_range.second);
ranges_.erase(start, ++end);
ranges_.insert(Range(new_start, new_end));
}
void RangeSet::Union(const RangeSet& range_set) {
- ASSERT(&range_set != this);
+ DCHECK(&range_set != this);
for (const auto& it : range_set.ranges())
Union(it);
}
diff --git a/testing/range_set.h b/testing/range_set.h
index 6ed24bd..7ba10df 100644
--- a/testing/range_set.h
+++ b/testing/range_set.h
@@ -1,4 +1,4 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
diff --git a/testing/resources/CMYK-alpha.jpf b/testing/resources/CMYK-alpha.jpf
new file mode 100644
index 0000000..538e6d6
--- /dev/null
+++ b/testing/resources/CMYK-alpha.jpf
Binary files differ
diff --git a/testing/resources/CMYK.jpf b/testing/resources/CMYK.jpf
new file mode 100644
index 0000000..00f2ef9
--- /dev/null
+++ b/testing/resources/CMYK.jpf
Binary files differ
diff --git a/testing/resources/RGB-alpha.jp2 b/testing/resources/RGB-alpha.jp2
new file mode 100644
index 0000000..4ab41da
--- /dev/null
+++ b/testing/resources/RGB-alpha.jp2
Binary files differ
diff --git a/testing/resources/RGB.jp2 b/testing/resources/RGB.jp2
new file mode 100644
index 0000000..7b7d428
--- /dev/null
+++ b/testing/resources/RGB.jp2
Binary files differ
diff --git a/testing/resources/annot_javascript.in b/testing/resources/annot_javascript.in
new file mode 100644
index 0000000..fe9ac46
--- /dev/null
+++ b/testing/resources/annot_javascript.in
@@ -0,0 +1,42 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /AcroForm <<
+ /Fields [4 0 R]
+ >>
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /MediaBox [0 0 612 792]
+ /Kids [3 0 R]
+ /Count 1
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 612 792]
+ /Annots [4 0 R]
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /Rect [85 721 153 735]
+ /FT /Tx
+ /P 3 0 R
+ /T (Widget)
+ /AA <<
+ /F <<
+ /JS (AFDate_FormatEx\("yyyy-mm-dd"\);)
+ /S /JavaScript
+ >>
+ >>
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/annot_javascript.pdf b/testing/resources/annot_javascript.pdf
new file mode 100644
index 0000000..fbeb5df
--- /dev/null
+++ b/testing/resources/annot_javascript.pdf
@@ -0,0 +1,53 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /AcroForm <<
+ /Fields [4 0 R]
+ >>
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /MediaBox [0 0 612 792]
+ /Kids [3 0 R]
+ /Count 1
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 612 792]
+ /Annots [4 0 R]
+>>
+endobj
+4 0 obj <<
+ /Type /Annot
+ /Subtype /Widget
+ /Rect [85 721 153 735]
+ /FT /Tx
+ /P 3 0 R
+ /T (Widget)
+ /AA <<
+ /F <<
+ /JS (AFDate_FormatEx\("yyyy-mm-dd"\);)
+ /S /JavaScript
+ >>
+ >>
+>>
+endobj
+xref
+0 5
+0000000000 65535 f
+0000000015 00000 n
+0000000108 00000 n
+0000000197 00000 n
+0000000292 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 5
+>>
+startxref
+504
+%%EOF
diff --git a/testing/resources/annots.in b/testing/resources/annots.in
new file mode 100644
index 0000000..cf575f5
--- /dev/null
+++ b/testing/resources/annots.in
@@ -0,0 +1,362 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /AcroForm <<
+ /Fields [23 0 R]
+ /DR <<
+ /Font <<
+ /F1 7 0 R
+ >>
+ >>
+ >>
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 2
+ /Kids [3 0 R 4 0 R]
+ /MediaBox [0 0 612 792]
+ /CropBox [0 0 612 792]
+ /Resources <<
+ /Font <<
+ /F1 7 0 R
+ /F2 8 0 R
+ >>
+ /ProcSet [/PDF /Text /ImageC]
+ /ExtGState <<
+ /GS0 24 0 R
+ >>
+ >>
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 5 0 R
+ /Annots [15 0 R 16 0 R 17 0 R 18 0 R 19 0 R 20 0 R 21 0 R 22 0 R 23 0 R]
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 6 0 R
+ /Annots [15 0 R 16 0 R 26 0 R]
+>>
+endobj
+{{object 5 0}} <<
+ {{streamlen}}
+>>
+stream
+BT
+70 700 Td
+/F1 18 Tf
+(Link Annotations - Page 1) Tj
+0 -65 Td
+/F2 14 Tf
+(1. Link with destination to first page) Tj
+10 -20 Td
+/F2 14 Tf
+(2. Link with destination to second page) Tj
+-12 -84 Td
+/F2 10 Tf
+(PDF Reference, Version 1.7, Section 8.4.5 defines Annotations) Tj
+2 -53 Td
+(3. An example of Highlight with text notes) Tj
+0 -18 Td
+(https://pdfium.googlesource.com/pdfium is link in plain text, not link annotation. These are referred to) Tj
+0 -17 Td
+(as WebLinks in PDFium.)Tj
+ET
+endstream
+endobj
+{{object 6 0}} <<
+ {{streamlen}}
+>>
+stream
+BT
+70 700 Td
+/F1 18 Tf
+(Link Annotations - Page 2) Tj
+0 -65 Td
+/F2 14 Tf
+(1. Link with destination to first page) Tj
+10 -20 Td
+/F2 14 Tf
+(2. Link with destination to second page) Tj
+ET
+endstream
+endobj
+{{object 7 0}} <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Times-Roman
+>>
+endobj
+{{object 8 0}} <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Helvetica
+>>
+endobj
+{{object 9 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ {{streamlen}}
+ /BBox [293 530 349 542]
+ /Resources <<
+ /XObject <<
+ /Form0 10 0 R
+ >>
+ /ExtGState <<
+ /GS0 25 0 R
+ >>
+ >>
+>>
+stream
+/GS0 gs
+/Form0 Do
+endstream
+endobj
+{{object 10 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /Group <<
+ /S /Transparency
+ >>
+ {{streamlen}}
+ /BBox [293 530 349 542]
+>>
+stream
+1.0 1.0 0.0 rg
+293 530 m
+349 530 l
+349 542 l
+293 542 l
+h f
+endstream
+endobj
+{{object 11 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ {{streamlen}}
+ /BBox [83 440 178 453]
+ /Resources <<
+ /XObject <<
+ /Form0 12 0 R
+ >>
+ /ExtGState <<
+ /GS0 25 0 R
+ >>
+ >>
+>>
+stream
+/GS0 gs
+/Form0 Do
+endstream
+endobj
+{{object 12 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /Group <<
+ /S /Transparency
+ >>
+ {{streamlen}}
+ /BBox [83 440 178 453]
+>>
+stream
+0.0 1.0 1.0 rg
+83 440 m
+178 440 l
+178 453 l
+83 453 l
+h f
+endstream
+endobj
+{{object 13 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ {{streamlen}}
+ /BBox [149 476 191 487]
+ /Resources <<
+ /XObject <<
+ /Form0 14 0 R
+ >>
+ /ExtGState <<
+ /GS0 25 0 R
+ >>
+ >>
+>>
+stream
+/GS0 gs
+/Form0 Do
+endstream
+endobj
+{{object 14 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /Group <<
+ /S /Transparency
+ >>
+ {{streamlen}}
+ /BBox [149 476 191 487]
+>>
+stream
+0.0 1.0 0.0 rg
+149 476 m
+191 476 l
+191 487 l
+149 487 l
+h f
+endstream
+endobj
+{{object 15 0}} <<
+ /Type /Annot
+ /Subtype /Link
+ /BS <<
+ /W 0
+ >>
+ /Rect [69 633 542 653]
+ /Dest [3 0 R /XYZ 200 725 0]
+ /F 4
+>>
+endobj
+{{object 16 0}} <<
+ /Type /Annot
+ /Subtype /Link
+ /BS <<
+ /W 0
+ >>
+ /Rect [80 613 542 633]
+ /Dest [4 0 R /XYZ 200 725 0]
+ /F 4
+>>
+endobj
+{{object 17 0}} <<
+ /Type /Annot
+ /Subtype /Link
+ /BS <<
+ /W 0
+ >>
+ /Rect [66 529 196 544]
+ /A <<
+ /Type /Action
+ /URI (https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/pdf_reference_1-7.pdf)
+ /S /URI
+ >>
+ /F 4
+>>
+endobj
+{{object 18 0}} <<
+ /Type /Annot
+ /Subtype /Link
+ /BS <<
+ /W 0
+ >>
+ /Rect [83 440 178 453]
+ /QuadPoints [83 453 178 453 83 440 178 440]
+ /A <<
+ /Type /Action
+ /URI (https://cs.chromium.org/chromium/src/third_party/pdfium/public/fpdf_text.h)
+ /S /URI
+ >>
+ /F 4
+>>
+endobj
+{{object 19 0}} <<
+ /Type /Annot
+ /Subtype /Highlight
+ /AP <<
+ /N 9 0 R
+ >>
+ /NM (Highlight-1)
+ /F 4
+ /QuadPoints [293 542 349 542 293 530 349 530]
+ /P 3 0 R
+ /C [1 0.90196 0]
+ /Rect [293 530 349 542]
+>>
+endobj
+{{object 20 0}} <<
+ /Type /Annot
+ /Subtype /Highlight
+ /AP <<
+ /N 11 0 R
+ >>
+ /NM (Highlight-2)
+ /F 4
+ /QuadPoints [83 453 178 453 83 440 178 440]
+ /P 3 0 R
+ /C [0.26667 0.78431 0.96078]
+ /Rect [83 440 178 453]
+>>
+endobj
+{{object 21 0}} <<
+ /Type /Annot
+ /Subtype /Popup
+ /Parent 22 0 R
+ /Rect [191 377 443 488]
+>>
+endobj
+{{object 22 0}} <<
+ /Type /Annot
+ /Subtype /Highlight
+ /Popup 21 0 R
+ /AP <<
+ /N 13 0 R
+ >>
+ /NM (Highlight-With-Popup-1)
+ /Contents (Text Note)
+ /QuadPoints [149 487 191 487 149 476 191 476]
+ /P 3 0 R
+ /C [0.14902 0.90196 0]
+ /Rect [149 476 191 487]
+ /F 4
+>>
+endobj
+{{object 23 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Ch
+ /Ff 131072
+ /T (Combo1)
+ /DA (0 0 0 rg /F1 12 Tf)
+ /Rect [70 350 170 380]
+ /Opt [(Highlight) (Link) (Popup) (Widget)]
+>>
+endobj
+{{object 24 0}} <<
+ /ca 1
+ /Type /ExtGState
+ /CA 1
+ /BM /Normal
+>>
+endobj
+{{object 25 0}} <<
+ /ca 1
+ /Type /ExtGState
+ /CA 1
+ /AIS false
+ /BM /Multiply
+>>
+endobj
+{{object 26 0}} <<
+ /Type /Annot
+ /Subtype /Square
+ /Border [0 0 2]
+ /C [1 0 0]
+ /F 4
+ /P 3 0 R
+ /Rect [50 100 60 120]
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/annots.pdf b/testing/resources/annots.pdf
new file mode 100644
index 0000000..745346c
--- /dev/null
+++ b/testing/resources/annots.pdf
@@ -0,0 +1,395 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /AcroForm <<
+ /Fields [23 0 R]
+ /DR <<
+ /Font <<
+ /F1 7 0 R
+ >>
+ >>
+ >>
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /Count 2
+ /Kids [3 0 R 4 0 R]
+ /MediaBox [0 0 612 792]
+ /CropBox [0 0 612 792]
+ /Resources <<
+ /Font <<
+ /F1 7 0 R
+ /F2 8 0 R
+ >>
+ /ProcSet [/PDF /Text /ImageC]
+ /ExtGState <<
+ /GS0 24 0 R
+ >>
+ >>
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 5 0 R
+ /Annots [15 0 R 16 0 R 17 0 R 18 0 R 19 0 R 20 0 R 21 0 R 22 0 R 23 0 R]
+>>
+endobj
+4 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 6 0 R
+ /Annots [15 0 R 16 0 R 26 0 R]
+>>
+endobj
+5 0 obj <<
+ /Length 486
+>>
+stream
+BT
+70 700 Td
+/F1 18 Tf
+(Link Annotations - Page 1) Tj
+0 -65 Td
+/F2 14 Tf
+(1. Link with destination to first page) Tj
+10 -20 Td
+/F2 14 Tf
+(2. Link with destination to second page) Tj
+-12 -84 Td
+/F2 10 Tf
+(PDF Reference, Version 1.7, Section 8.4.5 defines Annotations) Tj
+2 -53 Td
+(3. An example of Highlight with text notes) Tj
+0 -18 Td
+(https://pdfium.googlesource.com/pdfium is link in plain text, not link annotation. These are referred to) Tj
+0 -17 Td
+(as WebLinks in PDFium.)Tj
+ET
+endstream
+endobj
+6 0 obj <<
+ /Length 185
+>>
+stream
+BT
+70 700 Td
+/F1 18 Tf
+(Link Annotations - Page 2) Tj
+0 -65 Td
+/F2 14 Tf
+(1. Link with destination to first page) Tj
+10 -20 Td
+/F2 14 Tf
+(2. Link with destination to second page) Tj
+ET
+endstream
+endobj
+7 0 obj <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Times-Roman
+>>
+endobj
+8 0 obj <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Helvetica
+>>
+endobj
+9 0 obj <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /Length 18
+ /BBox [293 530 349 542]
+ /Resources <<
+ /XObject <<
+ /Form0 10 0 R
+ >>
+ /ExtGState <<
+ /GS0 25 0 R
+ >>
+ >>
+>>
+stream
+/GS0 gs
+/Form0 Do
+endstream
+endobj
+10 0 obj <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /Group <<
+ /S /Transparency
+ >>
+ /Length 59
+ /BBox [293 530 349 542]
+>>
+stream
+1.0 1.0 0.0 rg
+293 530 m
+349 530 l
+349 542 l
+293 542 l
+h f
+endstream
+endobj
+11 0 obj <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /Length 18
+ /BBox [83 440 178 453]
+ /Resources <<
+ /XObject <<
+ /Form0 12 0 R
+ >>
+ /ExtGState <<
+ /GS0 25 0 R
+ >>
+ >>
+>>
+stream
+/GS0 gs
+/Form0 Do
+endstream
+endobj
+12 0 obj <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /Group <<
+ /S /Transparency
+ >>
+ /Length 57
+ /BBox [83 440 178 453]
+>>
+stream
+0.0 1.0 1.0 rg
+83 440 m
+178 440 l
+178 453 l
+83 453 l
+h f
+endstream
+endobj
+13 0 obj <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /Length 18
+ /BBox [149 476 191 487]
+ /Resources <<
+ /XObject <<
+ /Form0 14 0 R
+ >>
+ /ExtGState <<
+ /GS0 25 0 R
+ >>
+ >>
+>>
+stream
+/GS0 gs
+/Form0 Do
+endstream
+endobj
+14 0 obj <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /Group <<
+ /S /Transparency
+ >>
+ /Length 59
+ /BBox [149 476 191 487]
+>>
+stream
+0.0 1.0 0.0 rg
+149 476 m
+191 476 l
+191 487 l
+149 487 l
+h f
+endstream
+endobj
+15 0 obj <<
+ /Type /Annot
+ /Subtype /Link
+ /BS <<
+ /W 0
+ >>
+ /Rect [69 633 542 653]
+ /Dest [3 0 R /XYZ 200 725 0]
+ /F 4
+>>
+endobj
+16 0 obj <<
+ /Type /Annot
+ /Subtype /Link
+ /BS <<
+ /W 0
+ >>
+ /Rect [80 613 542 633]
+ /Dest [4 0 R /XYZ 200 725 0]
+ /F 4
+>>
+endobj
+17 0 obj <<
+ /Type /Annot
+ /Subtype /Link
+ /BS <<
+ /W 0
+ >>
+ /Rect [66 529 196 544]
+ /A <<
+ /Type /Action
+ /URI (https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/pdf_reference_1-7.pdf)
+ /S /URI
+ >>
+ /F 4
+>>
+endobj
+18 0 obj <<
+ /Type /Annot
+ /Subtype /Link
+ /BS <<
+ /W 0
+ >>
+ /Rect [83 440 178 453]
+ /QuadPoints [83 453 178 453 83 440 178 440]
+ /A <<
+ /Type /Action
+ /URI (https://cs.chromium.org/chromium/src/third_party/pdfium/public/fpdf_text.h)
+ /S /URI
+ >>
+ /F 4
+>>
+endobj
+19 0 obj <<
+ /Type /Annot
+ /Subtype /Highlight
+ /AP <<
+ /N 9 0 R
+ >>
+ /NM (Highlight-1)
+ /F 4
+ /QuadPoints [293 542 349 542 293 530 349 530]
+ /P 3 0 R
+ /C [1 0.90196 0]
+ /Rect [293 530 349 542]
+>>
+endobj
+20 0 obj <<
+ /Type /Annot
+ /Subtype /Highlight
+ /AP <<
+ /N 11 0 R
+ >>
+ /NM (Highlight-2)
+ /F 4
+ /QuadPoints [83 453 178 453 83 440 178 440]
+ /P 3 0 R
+ /C [0.26667 0.78431 0.96078]
+ /Rect [83 440 178 453]
+>>
+endobj
+21 0 obj <<
+ /Type /Annot
+ /Subtype /Popup
+ /Parent 22 0 R
+ /Rect [191 377 443 488]
+>>
+endobj
+22 0 obj <<
+ /Type /Annot
+ /Subtype /Highlight
+ /Popup 21 0 R
+ /AP <<
+ /N 13 0 R
+ >>
+ /NM (Highlight-With-Popup-1)
+ /Contents (Text Note)
+ /QuadPoints [149 487 191 487 149 476 191 476]
+ /P 3 0 R
+ /C [0.14902 0.90196 0]
+ /Rect [149 476 191 487]
+ /F 4
+>>
+endobj
+23 0 obj <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Ch
+ /Ff 131072
+ /T (Combo1)
+ /DA (0 0 0 rg /F1 12 Tf)
+ /Rect [70 350 170 380]
+ /Opt [(Highlight) (Link) (Popup) (Widget)]
+>>
+endobj
+24 0 obj <<
+ /ca 1
+ /Type /ExtGState
+ /CA 1
+ /BM /Normal
+>>
+endobj
+25 0 obj <<
+ /ca 1
+ /Type /ExtGState
+ /CA 1
+ /AIS false
+ /BM /Multiply
+>>
+endobj
+26 0 obj <<
+ /Type /Annot
+ /Subtype /Square
+ /Border [0 0 2]
+ /C [1 0 0]
+ /F 4
+ /P 3 0 R
+ /Rect [50 100 60 120]
+>>
+endobj
+xref
+0 27
+0000000000 65535 f
+0000000015 00000 n
+0000000169 00000 n
+0000000439 00000 n
+0000000583 00000 n
+0000000685 00000 n
+0000001223 00000 n
+0000001460 00000 n
+0000001538 00000 n
+0000001614 00000 n
+0000001864 00000 n
+0000002087 00000 n
+0000002337 00000 n
+0000002557 00000 n
+0000002808 00000 n
+0000003031 00000 n
+0000003171 00000 n
+0000003311 00000 n
+0000003558 00000 n
+0000003842 00000 n
+0000004059 00000 n
+0000004286 00000 n
+0000004384 00000 n
+0000004659 00000 n
+0000004849 00000 n
+0000004920 00000 n
+0000005006 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 27
+>>
+startxref
+5135
+%%EOF
diff --git a/testing/resources/annots_action_handling.in b/testing/resources/annots_action_handling.in
new file mode 100644
index 0000000..e5c728b
--- /dev/null
+++ b/testing/resources/annots_action_handling.in
@@ -0,0 +1,132 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /Names 12 0 R
+ /AcroForm [6 0 R 7 0 R]
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 2
+ /Kids [3 0 R 4 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Annots [6 0 R 7 0 R 8 0 R 9 0 R 10 0 R 11 0 R]
+ /Contents 5 0 R
+ /Tabs /R
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Tabs /R
+>>
+endobj
+{{object 5 0}} <<
+ {{streamlen}}
+>>
+stream
+BT
+70 340 Td
+14 Tf
+(External Link ) Tj
+0 -35 Td
+14 Tf
+(Internal Link ) Tj
+0 -35 Td
+14 Tf
+(Link1 to top ) Tj
+0 -35 Td
+14 Tf
+(Link2 to top ) Tj
+ET
+endstream
+endobj
+{{object 6 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Tx
+ /Parent 3 0 R
+ /T (TextField)
+ /Rect [69 670 220 690]
+>>
+endobj
+{{object 7 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Btn
+ /Rect [69 360 220 380]
+ /A <<
+ /URI (https://www.google.com)
+ /S /URI
+ >>
+ /F 4
+ /T (button)
+ /Ff 65536
+>>
+endobj
+{{object 8 0}} <<
+ /Type /Annot
+ /Subtype /Link
+ /Rect [69 338 180 358]
+ /A <<
+ /Type /Action
+ /S /URI
+ /URI (https://cs.chromium.org/)
+ >>
+ /F 4
+>>
+endobj
+{{object 9 0}} <<
+ /Type /Annot
+ /Subtype /Link
+ /Rect [69 305 180 325]
+ /BS <<
+ /W 0
+ >>
+ /Dest [4 0 R /XYZ 200 725 0]
+ /F 4
+>>
+{{object 10 0}} <<
+ /Type /Annot
+ /Subtype /Link
+ /Rect [69 270 180 290]
+ /BS <<
+ /W 0
+ >>
+ /Dest /top
+ /F 4
+>>
+{{object 11 0}} <<
+ /Type /Annot
+ /Subtype /Link
+ /Rect [69 235 180 255]
+ /BS <<
+ /W 0
+ >>
+ /Dest (target10)
+ /F 4
+>>
+{{object 12 0}} <<
+ /Dests 13 0 R
+>>
+endobj
+{{object 13 0}} <<
+ /Names [
+ (target10) 14 0 R
+ /top 14 0 R
+ ]
+>>
+endobj
+{{object 14 0}} <<
+ /D [3 0 R /XYZ 100 200 0]
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
\ No newline at end of file
diff --git a/testing/resources/annots_action_handling.pdf b/testing/resources/annots_action_handling.pdf
new file mode 100644
index 0000000..b8b6c2a
--- /dev/null
+++ b/testing/resources/annots_action_handling.pdf
@@ -0,0 +1,153 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /Names 12 0 R
+ /AcroForm [6 0 R 7 0 R]
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /Count 2
+ /Kids [3 0 R 4 0 R]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Annots [6 0 R 7 0 R 8 0 R 9 0 R 10 0 R 11 0 R]
+ /Contents 5 0 R
+ /Tabs /R
+>>
+endobj
+4 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Tabs /R
+>>
+endobj
+5 0 obj <<
+ /Length 145
+>>
+stream
+BT
+70 340 Td
+14 Tf
+(External Link ) Tj
+0 -35 Td
+14 Tf
+(Internal Link ) Tj
+0 -35 Td
+14 Tf
+(Link1 to top ) Tj
+0 -35 Td
+14 Tf
+(Link2 to top ) Tj
+ET
+endstream
+endobj
+6 0 obj <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Tx
+ /Parent 3 0 R
+ /T (TextField)
+ /Rect [69 670 220 690]
+>>
+endobj
+7 0 obj <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Btn
+ /Rect [69 360 220 380]
+ /A <<
+ /URI (https://www.google.com)
+ /S /URI
+ >>
+ /F 4
+ /T (button)
+ /Ff 65536
+>>
+endobj
+8 0 obj <<
+ /Type /Annot
+ /Subtype /Link
+ /Rect [69 338 180 358]
+ /A <<
+ /Type /Action
+ /S /URI
+ /URI (https://cs.chromium.org/)
+ >>
+ /F 4
+>>
+endobj
+9 0 obj <<
+ /Type /Annot
+ /Subtype /Link
+ /Rect [69 305 180 325]
+ /BS <<
+ /W 0
+ >>
+ /Dest [4 0 R /XYZ 200 725 0]
+ /F 4
+>>
+10 0 obj <<
+ /Type /Annot
+ /Subtype /Link
+ /Rect [69 270 180 290]
+ /BS <<
+ /W 0
+ >>
+ /Dest /top
+ /F 4
+>>
+11 0 obj <<
+ /Type /Annot
+ /Subtype /Link
+ /Rect [69 235 180 255]
+ /BS <<
+ /W 0
+ >>
+ /Dest (target10)
+ /F 4
+>>
+12 0 obj <<
+ /Dests 13 0 R
+>>
+endobj
+13 0 obj <<
+ /Names [
+ (target10) 14 0 R
+ /top 14 0 R
+ ]
+>>
+endobj
+14 0 obj <<
+ /D [3 0 R /XYZ 100 200 0]
+>>
+endobj
+xref
+0 15
+0000000000 65535 f
+0000000015 00000 n
+0000000110 00000 n
+0000000179 00000 n
+0000000309 00000 n
+0000000371 00000 n
+0000000568 00000 n
+0000000691 00000 n
+0000000874 00000 n
+0000001038 00000 n
+0000001170 00000 n
+0000001285 00000 n
+0000001406 00000 n
+0000001444 00000 n
+0000001519 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 15
+>>
+startxref
+1569
+%%EOF
\ No newline at end of file
diff --git a/testing/resources/bad_dict_keys.in b/testing/resources/bad_dict_keys.in
new file mode 100644
index 0000000..3f53c28
--- /dev/null
+++ b/testing/resources/bad_dict_keys.in
@@ -0,0 +1,23 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /MediaBox [0 0 300 300]
+ /Count 1
+ /Kids [3 0 R]
+ / [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bad_dict_keys.pdf b/testing/resources/bad_dict_keys.pdf
new file mode 100644
index 0000000..b55c434
--- /dev/null
+++ b/testing/resources/bad_dict_keys.pdf
@@ -0,0 +1,33 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /MediaBox [0 0 300 300]
+ /Count 1
+ /Kids [3 0 R]
+ / [3 0 R]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+>>
+endobj
+xref
+0 4
+0000000000 65535 f
+0000000015 00000 n
+0000000068 00000 n
+0000000169 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 4
+>>
+startxref
+220
+%%EOF
diff --git a/testing/resources/bigtable_mini.in b/testing/resources/bigtable_mini.in
new file mode 100644
index 0000000..7e80992
--- /dev/null
+++ b/testing/resources/bigtable_mini.in
@@ -0,0 +1,113 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /MediaBox [0 0 612 792]
+ /Resources <<
+ /ProcSet [/PDF /ImageB /Text]
+ /Font <<
+ /F1 5 0 R
+ /F2 6 0 R
+ >>
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+q BT
+1 0 0 1 250 667 Tm
+/F2 8.96638 Tf
+-243.635 -15.84 Td
+(f)Tj
+/F1 8.96638 Tf
+4.55491 0 Td
+(f)Tj
+2.87476 0 Td
+(ay)Tj
+7.79253 0 Td
+(,jef)Tj
+11.506 0 Td
+(f,sanjay)Tj
+27.4558 0 Td
+(,wilsonh,k)Tj
+37.1801 0 Td
+(err)Tj
+9.58372 0 Td
+(,m3b,tushar)Tj
+41.8537 0 Td
+(,k)Tj
+11.6349 0 Td
+(es,gruber)Tj
+/F2 8.96638 Tf
+32.9694 0 Td
+(g)Tj
+/F1 8.96638 Tf
+4.55491 0 Td
+(@google.com)Tj
+ET Q
+endstream
+endobj
+{{object 5 0}} <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Times-Roman
+>>
+endobj
+{{object 6 0}} <<
+ /Type /Font
+ /Subtype /Type1
+ /FirstChar 102
+ /BaseFont /RFSQHQ+CMSY9
+ /FontDescriptor 7 0 R
+ /LastChar 103
+ /Widths [508 508]
+>>
+endobj
+{{object 7 0}} <<
+ /Type /FontDescriptor
+ /Ascent 750
+ /CapHeight 750
+ /CharSet (/braceleft/braceright)
+ /Descent -250
+ /Flags 4
+ /FontBBox [0 -250 440 750]
+ /FontFile3 8 0 R
+ /FontName /RFSQHQ+CMSY9
+ /ItalicAngle 0
+ /StemV 65
+>>
+endobj
+{{object 8 0}} <<
+ /Subtype /Type1C
+ /Filter [/ASCII85Decode /FlateDecode]
+ {{streamlen}}
+>>
+stream
+GhQY<?t!MPA7Xa1EXNBL#h4%k%?@du#q5_5B)EgioL"p)pKs!j9@_A!X@n"A#^s/nr,0aY?!i,5R?>nA
+43BRuTX#t%4ekD2_c]pSd*g?I_(dmV-o3k<:Veup,U50*Ylr.i;$bHCc:fghe5L>1a\`<FkpTR<8hEdi
+.SEJ:>O%`N>>SLd>,:)GT9<BBa2#L+Pa9V1&Ao("^^P</!u&Ql7PhdY9T5#6IePGiOgOaNdLsRg)OHi+
+n+a.f[/4_7lnp:OXP)%6XER"WK`:C2*&F%pl[L]/LH0S#rJk:S[coEjaA0p=l`BI4BP[$LCn09862jKs
+Y)F7W^!5B(h.[kDUY*1W\e@l8mE%N?^8RN2qNUCce!bWPjLtF8=4Zg,R,1!:HR6^V/b\TIh0Z`11`hZZ
+*BO'Za1C[ph)dSm>aE#>k-A'_h4)!bqJD$jjj@/_bu*BN?.Ud@HV0Y&l1Vg$1F)e^]:6ER3IV(Tbj1;S
+BposQ0Df/MDiTcoYO4BSY\&*%<aJs1;J/:.>?>$UG7-pseF)R.T'a;@,PN5BX]sWQf<*o6H:RGZ*f^Pj
+qkd,*mVA#1lAJeH\%d!)GAm3[%_gAF5OB8mr>(pE^5_Fj[i?3Bq0X7/D)6E](`9a_gaUoK~>
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bigtable_mini.pdf b/testing/resources/bigtable_mini.pdf
new file mode 100644
index 0000000..adb3e02
--- /dev/null
+++ b/testing/resources/bigtable_mini.pdf
@@ -0,0 +1,128 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /MediaBox [0 0 612 792]
+ /Resources <<
+ /ProcSet [/PDF /ImageB /Text]
+ /Font <<
+ /F1 5 0 R
+ /F2 6 0 R
+ >>
+ >>
+>>
+endobj
+4 0 obj <<
+ /Length 374
+>>
+stream
+q BT
+1 0 0 1 250 667 Tm
+/F2 8.96638 Tf
+-243.635 -15.84 Td
+(f)Tj
+/F1 8.96638 Tf
+4.55491 0 Td
+(f)Tj
+2.87476 0 Td
+(ay)Tj
+7.79253 0 Td
+(,jef)Tj
+11.506 0 Td
+(f,sanjay)Tj
+27.4558 0 Td
+(,wilsonh,k)Tj
+37.1801 0 Td
+(err)Tj
+9.58372 0 Td
+(,m3b,tushar)Tj
+41.8537 0 Td
+(,k)Tj
+11.6349 0 Td
+(es,gruber)Tj
+/F2 8.96638 Tf
+32.9694 0 Td
+(g)Tj
+/F1 8.96638 Tf
+4.55491 0 Td
+(@google.com)Tj
+ET Q
+endstream
+endobj
+5 0 obj <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Times-Roman
+>>
+endobj
+6 0 obj <<
+ /Type /Font
+ /Subtype /Type1
+ /FirstChar 102
+ /BaseFont /RFSQHQ+CMSY9
+ /FontDescriptor 7 0 R
+ /LastChar 103
+ /Widths [508 508]
+>>
+endobj
+7 0 obj <<
+ /Type /FontDescriptor
+ /Ascent 750
+ /CapHeight 750
+ /CharSet (/braceleft/braceright)
+ /Descent -250
+ /Flags 4
+ /FontBBox [0 -250 440 750]
+ /FontFile3 8 0 R
+ /FontName /RFSQHQ+CMSY9
+ /ItalicAngle 0
+ /StemV 65
+>>
+endobj
+8 0 obj <<
+ /Subtype /Type1C
+ /Filter [/ASCII85Decode /FlateDecode]
+ /Length 640
+>>
+stream
+GhQY<?t!MPA7Xa1EXNBL#h4%k%?@du#q5_5B)EgioL"p)pKs!j9@_A!X@n"A#^s/nr,0aY?!i,5R?>nA
+43BRuTX#t%4ekD2_c]pSd*g?I_(dmV-o3k<:Veup,U50*Ylr.i;$bHCc:fghe5L>1a\`<FkpTR<8hEdi
+.SEJ:>O%`N>>SLd>,:)GT9<BBa2#L+Pa9V1&Ao("^^P</!u&Ql7PhdY9T5#6IePGiOgOaNdLsRg)OHi+
+n+a.f[/4_7lnp:OXP)%6XER"WK`:C2*&F%pl[L]/LH0S#rJk:S[coEjaA0p=l`BI4BP[$LCn09862jKs
+Y)F7W^!5B(h.[kDUY*1W\e@l8mE%N?^8RN2qNUCce!bWPjLtF8=4Zg,R,1!:HR6^V/b\TIh0Z`11`hZZ
+*BO'Za1C[ph)dSm>aE#>k-A'_h4)!bqJD$jjj@/_bu*BN?.Ud@HV0Y&l1Vg$1F)e^]:6ER3IV(Tbj1;S
+BposQ0Df/MDiTcoYO4BSY\&*%<aJs1;J/:.>?>$UG7-pseF)R.T'a;@,PN5BX]sWQf<*o6H:RGZ*f^Pj
+qkd,*mVA#1lAJeH\%d!)GAm3[%_gAF5OB8mr>(pE^5_Fj[i?3Bq0X7/D)6E](`9a_gaUoK~>
+endstream
+endobj
+xref
+0 9
+0000000000 65535 f
+0000000015 00000 n
+0000000068 00000 n
+0000000131 00000 n
+0000000333 00000 n
+0000000759 00000 n
+0000000837 00000 n
+0000000993 00000 n
+0000001234 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 9
+>>
+startxref
+1985
+%%EOF
diff --git a/testing/resources/bookmarks.in b/testing/resources/bookmarks.in
index 793f6ae..b0489a5 100644
--- a/testing/resources/bookmarks.in
+++ b/testing/resources/bookmarks.in
@@ -2,7 +2,7 @@
{{object 1 0}} <<
/Type /Catalog
/Pages 2 0 R
- /Outlines 14 0 R
+ /Outlines 8 0 R
>>
endobj
{{object 2 0}} <<
@@ -19,9 +19,11 @@
/Type /Page
/Parent 2 0 R
/Resources <<
- /Font <</F1 15 0 R>>
+ /Font <<
+ /F1 5 0 R
+ >>
>>
- /Contents [21 0 R]
+ /Contents [6 0 R]
/MediaBox [0 0 612 792]
>>
endobj
@@ -30,46 +32,24 @@
/Type /Page
/Parent 2 0 R
/Resources <<
- /Font <</F1 15 0 R>>
+ /Font <<
+ /F1 5 0 R
+ >>
>>
- /Contents [22 0 R]
+ /Contents [7 0 R]
/MediaBox [0 0 612 792]
>>
endobj
-% First bookmark
-{{object 10 0}} <<
- /Title (A Good Beginning)
- /Parent 14 0 R
- /Next 11 0 R
- /Dest (foo)
->>
-endobj
-% Last bookmark
-{{object 11 0}} <<
- /Title (A Good Ending)
- /Parent 14 0 R
- /Prev 10 0 R
- /Dest (bar)
->>
-endobj
-% Root bookmark
-{{object 14 0}} <<
- /Type /Outlines
- /First 10 0 R
- /Last 11 0 R
- /Count 2
->>
-endobj
% Font resource.
-{{object 15 0}} <<
+{{object 5 0}} <<
/Type /Font
/Subtype /Type1
/BaseFont /Arial
>>
endobj
% Content for page 0.
-{{object 21 0}} <<
- /Length 0
+{{object 6 0}} <<
+ {{streamlen}}
>>
stream
BT
@@ -79,8 +59,8 @@
endstream
endobj
% Content for page 1.
-{{object 22 0}} <<
- /Length 0
+{{object 7 0}} <<
+ {{streamlen}}
>>
stream
BT
@@ -89,6 +69,72 @@
ET
endstream
endobj
+% Root bookmark
+{{object 8 0}} <<
+ /Type /Outlines
+ /Count 3
+ /First 9 0 R
+ /Last 12 0 R
+>>
+endobj
+% First child bookmark (leaf node)
+{{object 9 0}} <<
+ /Title (A Good Beginning)
+ /Parent 8 0 R
+ /Next 10 0 R
+ /Dest (foo)
+>>
+endobj
+% Second child bookmark (open)
+{{object 10 0}} <<
+ /Title (Open Middle)
+ /Parent 8 0 R
+ /First 11 0 R
+ /Last 11 0 R
+ /Prev 9 0 R
+ /Next 12 0 R
+ /Count 1
+ /A <<
+ /Type /Action
+ /S /URI
+ /URI (https://theplay.test)
+ >>
+>>
+endobj
+% First grandchild bookmark
+{{object 11 0}} <<
+ /Title (Open Middle Descendant)
+ /Parent 10 0 R
+ /Dest [3 0 R /XYZ 100 200 0]
+>>
+endobj
+% Third child bookmark (closed)
+{{object 12 0}} <<
+ /Title (A Good Closed Ending)
+ /Parent 8 0 R
+ /First 13 0 R
+ /Last 14 0 R
+ /Prev 10 0 R
+ /Count -2
+ /Dest (bar)
+>>
+endobj
+% Second grandchild bookmark
+{{object 13 0}} <<
+ /Title (A Good Closed Ending Descendant)
+ /Parent 12 0 R
+ /Next 14 0 R
+ /Dest (bar)
+>>
+endobj
+% Third grandchild bookmark
+{{object 14 0}} <<
+ /Title (A Good Closed Ending Descendant 2)
+ /Parent 12 0 R
+ /Prev 13 0 R
+ /Dest (bar)
+>>
+endobj
{{xref}}
{{trailer}}
{{startxref}}
diff --git a/testing/resources/bookmarks.pdf b/testing/resources/bookmarks.pdf
index 8c2eb5a..757f859 100644
--- a/testing/resources/bookmarks.pdf
+++ b/testing/resources/bookmarks.pdf
@@ -3,7 +3,7 @@
1 0 obj <<
/Type /Catalog
/Pages 2 0 R
- /Outlines 14 0 R
+ /Outlines 8 0 R
>>
endobj
2 0 obj <<
@@ -20,9 +20,11 @@
/Type /Page
/Parent 2 0 R
/Resources <<
- /Font <</F1 15 0 R>>
+ /Font <<
+ /F1 5 0 R
+ >>
>>
- /Contents [21 0 R]
+ /Contents [6 0 R]
/MediaBox [0 0 612 792]
>>
endobj
@@ -31,46 +33,24 @@
/Type /Page
/Parent 2 0 R
/Resources <<
- /Font <</F1 15 0 R>>
+ /Font <<
+ /F1 5 0 R
+ >>
>>
- /Contents [22 0 R]
+ /Contents [7 0 R]
/MediaBox [0 0 612 792]
>>
endobj
-% First bookmark
-10 0 obj <<
- /Title (A Good Beginning)
- /Parent 14 0 R
- /Next 11 0 R
- /Dest (foo)
->>
-endobj
-% Last bookmark
-11 0 obj <<
- /Title (A Good Ending)
- /Parent 14 0 R
- /Prev 10 0 R
- /Dest (bar)
->>
-endobj
-% Root bookmark
-14 0 obj <<
- /Type /Outlines
- /First 10 0 R
- /Last 11 0 R
- /Count 2
->>
-endobj
% Font resource.
-15 0 obj <<
+5 0 obj <<
/Type /Font
/Subtype /Type1
/BaseFont /Arial
>>
endobj
% Content for page 0.
-21 0 obj <<
- /Length 0
+6 0 obj <<
+ /Length 37
>>
stream
BT
@@ -80,8 +60,8 @@
endstream
endobj
% Content for page 1.
-22 0 obj <<
- /Length 0
+7 0 obj <<
+ /Length 37
>>
stream
BT
@@ -90,32 +70,93 @@
ET
endstream
endobj
+% Root bookmark
+8 0 obj <<
+ /Type /Outlines
+ /Count 3
+ /First 9 0 R
+ /Last 12 0 R
+>>
+endobj
+% First child bookmark (leaf node)
+9 0 obj <<
+ /Title (A Good Beginning)
+ /Parent 8 0 R
+ /Next 10 0 R
+ /Dest (foo)
+>>
+endobj
+% Second child bookmark (open)
+10 0 obj <<
+ /Title (Open Middle)
+ /Parent 8 0 R
+ /First 11 0 R
+ /Last 11 0 R
+ /Prev 9 0 R
+ /Next 12 0 R
+ /Count 1
+ /A <<
+ /Type /Action
+ /S /URI
+ /URI (https://theplay.test)
+ >>
+>>
+endobj
+% First grandchild bookmark
+11 0 obj <<
+ /Title (Open Middle Descendant)
+ /Parent 10 0 R
+ /Dest [3 0 R /XYZ 100 200 0]
+>>
+endobj
+% Third child bookmark (closed)
+12 0 obj <<
+ /Title (A Good Closed Ending)
+ /Parent 8 0 R
+ /First 13 0 R
+ /Last 14 0 R
+ /Prev 10 0 R
+ /Count -2
+ /Dest (bar)
+>>
+endobj
+% Second grandchild bookmark
+13 0 obj <<
+ /Title (A Good Closed Ending Descendant)
+ /Parent 12 0 R
+ /Next 14 0 R
+ /Dest (bar)
+>>
+endobj
+% Third grandchild bookmark
+14 0 obj <<
+ /Title (A Good Closed Ending Descendant 2)
+ /Parent 12 0 R
+ /Prev 13 0 R
+ /Dest (bar)
+>>
+endobj
xref
-0 23
+0 15
0000000000 65535 f
0000000015 00000 n
-0000000087 00000 n
-0000000185 00000 n
-0000000346 00000 n
-0000000000 65535 f
-0000000000 65535 f
-0000000000 65535 f
-0000000000 65535 f
-0000000000 65535 f
-0000000508 00000 n
-0000000620 00000 n
-0000000000 65535 f
-0000000000 65535 f
-0000000729 00000 n
-0000000829 00000 n
-0000000000 65535 f
-0000000000 65535 f
-0000000000 65535 f
-0000000000 65535 f
-0000000000 65535 f
-0000000924 00000 n
-0000001034 00000 n
-trailer<< /Root 1 0 R /Size 23 >>
+0000000086 00000 n
+0000000184 00000 n
+0000000355 00000 n
+0000000527 00000 n
+0000000621 00000 n
+0000000731 00000 n
+0000000835 00000 n
+0000000950 00000 n
+0000001075 00000 n
+0000001310 00000 n
+0000001446 00000 n
+0000001617 00000 n
+0000001756 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 15
+>>
startxref
-1122
+1869
%%EOF
diff --git a/testing/resources/bug_1055869.in b/testing/resources/bug_1055869.in
new file mode 100644
index 0000000..26cbf5f
--- /dev/null
+++ b/testing/resources/bug_1055869.in
@@ -0,0 +1,62 @@
+{{header}}
+{{include xfa_catalog_1_0.fragment}}
+{{include xfa_object_2_0.fragment}}
+{{include xfa_preamble_3_0.fragment}}
+{{include xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+ {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/2.6/">
+ <subform layout="tb" locale="en_US" name="form1" restoreState="auto">
+ <pageSet>
+ <pageArea id="Page1" name="Page1">
+ <contentArea h="10.5in" w="8in" x="0.25in" y="0.25in"/>
+ <medium long="11in" short="8.5in" stock="letter"/>
+ </pageArea>
+ </pageSet>
+ <subform h="10.5in" w="8in" name="subform2">
+ <field h="10mm" name="choiceList0" w="50mm" x="5mm" y="50mm">
+ <ui>
+ <choiceList/>
+ </ui>
+ <items>
+ <text>Single</text>
+ </items>
+ </field>
+ <field name="choiceList1" h="200mm" w="200mm" x="1mm" y="1mm">
+ <ui>
+ <textEdit/>
+ </ui>
+ <value>
+ <text>pdfium</text>
+ </value>
+ <event activity="change">
+ <script contentType="application/x-javascript">
+ change_count += 1;
+ if (change_count == 2) {
+ f1 = xfa.resolveNode("xfa.form..choiceList0");
+ xfa.host.setFocus(f1);
+ xfa.template.remerge();
+ xfa.host.openList(f1);
+ }
+ </script>
+ </event>
+ </field>
+ </subform>
+ <event activity="docReady">
+ <script contentType="application/x-javascript">
+ change_count = 0;
+ </script>
+ </event>
+ </subform>
+</template>
+endstream
+endobj
+{{include xfa_locale_6_0.fragment}}
+{{include xfa_postamble_7_0.fragment}}
+{{include xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_1055869.pdf b/testing/resources/bug_1055869.pdf
new file mode 100644
index 0000000..832d029
--- /dev/null
+++ b/testing/resources/bug_1055869.pdf
@@ -0,0 +1,271 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /AcroForm 2 0 R
+ /Extensions <<
+ /ADBE <<
+ /BaseVersion /1.7
+ /ExtensionLevel 8
+ >>
+ >>
+ /NeedsRendering true
+ /Pages 8 0 R
+ /Type /Catalog
+>>
+endobj
+2 0 obj <<
+ /XFA [
+ (preamble)
+ 3 0 R
+ (config)
+ 4 0 R
+ (template)
+ 5 0 R
+ (localeSet)
+ 6 0 R
+ (postamble)
+ 7 0 R
+ ]
+>>
+endobj
+3 0 obj <<
+ /Length 124
+>>
+stream
+<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/" timeStamp="2018-02-23T21:37:11Z" uuid="21482798-7bf0-40a4-bc5d-3cefdccf32b5">
+endstream
+endobj
+4 0 obj <<
+ /Length 642
+>>
+stream
+<config xmlns="http://www.xfa.org/schema/xci/3.0/">
+<agent name="designer">
+ <destination>pdf</destination>
+ <pdf>
+ <fontInfo/>
+ </pdf>
+</agent>
+<present>
+ <pdf>
+ <version>1.7</version>
+ <adobeExtensionLevel>8</adobeExtensionLevel>
+ <renderPolicy>client</renderPolicy>
+ <scriptModel>XFA</scriptModel>
+ <interactive>1</interactive>
+ </pdf>
+ <xdp>
+ <packets>*</packets>
+ </xdp>
+ <destination>pdf</destination>
+ <script>
+ <runScripts>server</runScripts>
+ </script>
+</present>
+<acrobat>
+ <acrobat7>
+ <dynamicRender>required</dynamicRender>
+ </acrobat7>
+ <validate>preSubmit</validate>
+</acrobat>
+</config>
+endstream
+endobj
+5 0 obj <<
+ /Length 1361
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/2.6/">
+ <subform layout="tb" locale="en_US" name="form1" restoreState="auto">
+ <pageSet>
+ <pageArea id="Page1" name="Page1">
+ <contentArea h="10.5in" w="8in" x="0.25in" y="0.25in"/>
+ <medium long="11in" short="8.5in" stock="letter"/>
+ </pageArea>
+ </pageSet>
+ <subform h="10.5in" w="8in" name="subform2">
+ <field h="10mm" name="choiceList0" w="50mm" x="5mm" y="50mm">
+ <ui>
+ <choiceList/>
+ </ui>
+ <items>
+ <text>Single</text>
+ </items>
+ </field>
+ <field name="choiceList1" h="200mm" w="200mm" x="1mm" y="1mm">
+ <ui>
+ <textEdit/>
+ </ui>
+ <value>
+ <text>pdfium</text>
+ </value>
+ <event activity="change">
+ <script contentType="application/x-javascript">
+ change_count += 1;
+ if (change_count == 2) {
+ f1 = xfa.resolveNode("xfa.form..choiceList0");
+ xfa.host.setFocus(f1);
+ xfa.template.remerge();
+ xfa.host.openList(f1);
+ }
+ </script>
+ </event>
+ </field>
+ </subform>
+ <event activity="docReady">
+ <script contentType="application/x-javascript">
+ change_count = 0;
+ </script>
+ </event>
+ </subform>
+</template>
+endstream
+endobj
+6 0 obj <<
+ /Length 3455
+>>
+stream
+<localeSet xmlns="http://www.xfa.org/schema/xfa-locale-set/2.7/">
+ <locale name="en_US" desc="English (United States)">
+ <calendarSymbols name="gregorian">
+ <monthNames>
+ <month>January</month>
+ <month>February</month>
+ <month>March</month>
+ <month>April</month>
+ <month>May</month>
+ <month>June</month>
+ <month>July</month>
+ <month>August</month>
+ <month>September</month>
+ <month>October</month>
+ <month>November</month>
+ <month>December</month>
+ </monthNames>
+ <monthNames abbr="1">
+ <month>Jan</month>
+ <month>Feb</month>
+ <month>Mar</month>
+ <month>Apr</month>
+ <month>May</month>
+ <month>Jun</month>
+ <month>Jul</month>
+ <month>Aug</month>
+ <month>Sep</month>
+ <month>Oct</month>
+ <month>Nov</month>
+ <month>Dec</month>
+ </monthNames>
+ <dayNames>
+ <day>Sunday</day>
+ <day>Monday</day>
+ <day>Tuesday</day>
+ <day>Wednesday</day>
+ <day>Thursday</day>
+ <day>Friday</day>
+ <day>Saturday</day>
+ </dayNames>
+ <dayNames abbr="1">
+ <day>Sun</day>
+ <day>Mon</day>
+ <day>Tue</day>
+ <day>Wed</day>
+ <day>Thu</day>
+ <day>Fri</day>
+ <day>Sat</day>
+ </dayNames>
+ <meridiemNames>
+ <meridiem>AM</meridiem>
+ <meridiem>PM</meridiem>
+ </meridiemNames>
+ <eraNames>
+ <era>BC</era>
+ <era>AD</era>
+ </eraNames>
+ </calendarSymbols>
+ <datePatterns>
+ <datePattern name="full">EEEE, MMMM D, YYYY</datePattern>
+ <datePattern name="long">MMMM D, YYYY</datePattern>
+ <datePattern name="med">MMM D, YYYY</datePattern>
+ <datePattern name="short">M/D/YY</datePattern>
+ </datePatterns>
+ <timePatterns>
+ <timePattern name="full">h:MM:SS A Z</timePattern>
+ <timePattern name="long">h:MM:SS A Z</timePattern>
+ <timePattern name="med">h:MM:SS A</timePattern>
+ <timePattern name="short">h:MM A</timePattern>
+ </timePatterns>
+ <dateTimeSymbols>GyMdkHmsSEDFwWahKzZ</dateTimeSymbols>
+ <numberPatterns>
+ <numberPattern name="numeric">z,zz9.zzz</numberPattern>
+ <numberPattern name="currency">$z,zz9.99|($z,zz9.99)</numberPattern>
+ <numberPattern name="percent">z,zz9%</numberPattern>
+ </numberPatterns>
+ <numberSymbols>
+ <numberSymbol name="decimal">.</numberSymbol>
+ <numberSymbol name="grouping">,</numberSymbol>
+ <numberSymbol name="percent">%</numberSymbol>
+ <numberSymbol name="minus">-</numberSymbol>
+ <numberSymbol name="zero">0</numberSymbol>
+ </numberSymbols>
+ <currencySymbols>
+ <currencySymbol name="symbol">$</currencySymbol>
+ <currencySymbol name="isoname">USD</currencySymbol>
+ <currencySymbol name="decimal">.</currencySymbol>
+ </currencySymbols>
+ <typefaces>
+ <typeface name="Myriad Pro"/>
+ <typeface name="Minion Pro"/>
+ <typeface name="Courier Std"/>
+ <typeface name="Adobe Pi Std"/>
+ <typeface name="Adobe Hebrew"/>
+ <typeface name="Adobe Arabic"/>
+ <typeface name="Adobe Thai"/>
+ <typeface name="Kozuka Gothic Pro-VI M"/>
+ <typeface name="Kozuka Mincho Pro-VI R"/>
+ <typeface name="Adobe Ming Std L"/>
+ <typeface name="Adobe Song Std L"/>
+ <typeface name="Adobe Myungjo Std M"/>
+ </typefaces>
+ </locale>
+</localeSet>
+endstream
+endobj
+7 0 obj <<
+ /Length 11
+>>
+stream
+</xdp:xdp>
+endstream
+endobj
+8 0 obj <<
+ /Type /Pages
+ /Count 1
+ /Kids [9 0 R]
+>>
+endobj
+9 0 obj <<
+ /Type /Page
+ /Parent 8 0 R
+ /MediaBox [0 0 612 792]
+>>
+endobj
+xref
+0 10
+0000000000 65535 f
+0000000015 00000 n
+0000000199 00000 n
+0000000358 00000 n
+0000000534 00000 n
+0000001228 00000 n
+0000002642 00000 n
+0000006150 00000 n
+0000006212 00000 n
+0000006275 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 10
+>>
+startxref
+6352
+%%EOF
diff --git a/testing/resources/bug_1058653.in b/testing/resources/bug_1058653.in
new file mode 100644
index 0000000..5973c54
--- /dev/null
+++ b/testing/resources/bug_1058653.in
@@ -0,0 +1,73 @@
+{{header}}
+{{include xfa_catalog_1_0.fragment}}
+{{include xfa_object_2_0.fragment}}
+{{include xfa_preamble_3_0.fragment}}
+{{include xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+ {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/2.6/">
+ <subform layout="tb" locale="en_US" name="form1" restoreState="auto">
+ <pageSet>
+ <pageArea id="Page1" name="Page1">
+ <contentArea h="10.5in" w="8in" x="0.25in" y="0.25in"/>
+ <medium long="11in" short="8.5in" stock="letter"/>
+ </pageArea>
+ </pageSet>
+ <subform h="10.5in" w="8in" name="subform2">
+ <field name="f1" h="10mm" w="10mm" x="20mm" y="20mm">
+ <ui>
+ <choiceList/>
+ </ui>
+ <items save="1">
+ <text>aaaaaaaaaa</text>
+ </items>
+ </field>
+ <subform name="f4" x="1mm" y="1mm">
+ <occur max="-1"/>
+ <field name="f2" h="350mm" w="200mm">
+ <ui>
+ <choiceList textEntry="1">
+ </choiceList>
+ </ui>
+ <items>
+ <text>Albania</text>
+ <text>Andorra</text>
+ <text>Armenia</text>
+ </items>
+ <event activity="change">
+ <script contentType="application/x-javascript">
+ a += 1;
+ if (a == 2) {
+ c = xfa.resolveNode("xfa.form..f1");
+ xfa.host.setFocus(c);
+ d = xfa.resolveNode("xfa.form..f4");
+ d.instanceManager.addInstance(1);
+ d.instanceManager.removeInstance(0);
+ xfa.host.openList(c);
+ }
+ </script>
+ </event>
+ </field>
+ </subform>
+ </subform>
+ <event activity="docReady">
+ <script contentType="application/x-javascript">
+ a = 0;
+ f2 = xfa.resolveNode("xfa.form..f2");
+ f2.rawValue = "minhtttttt";
+ xfa.host.setFocus(f2);
+ </script>
+ </event>
+ </subform>
+</template>
+endstream
+endobj
+{{include xfa_locale_6_0.fragment}}
+{{include xfa_postamble_7_0.fragment}}
+{{include xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_1058653.pdf b/testing/resources/bug_1058653.pdf
new file mode 100644
index 0000000..f278dfb
--- /dev/null
+++ b/testing/resources/bug_1058653.pdf
@@ -0,0 +1,282 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /AcroForm 2 0 R
+ /Extensions <<
+ /ADBE <<
+ /BaseVersion /1.7
+ /ExtensionLevel 8
+ >>
+ >>
+ /NeedsRendering true
+ /Pages 8 0 R
+ /Type /Catalog
+>>
+endobj
+2 0 obj <<
+ /XFA [
+ (preamble)
+ 3 0 R
+ (config)
+ 4 0 R
+ (template)
+ 5 0 R
+ (localeSet)
+ 6 0 R
+ (postamble)
+ 7 0 R
+ ]
+>>
+endobj
+3 0 obj <<
+ /Length 124
+>>
+stream
+<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/" timeStamp="2018-02-23T21:37:11Z" uuid="21482798-7bf0-40a4-bc5d-3cefdccf32b5">
+endstream
+endobj
+4 0 obj <<
+ /Length 642
+>>
+stream
+<config xmlns="http://www.xfa.org/schema/xci/3.0/">
+<agent name="designer">
+ <destination>pdf</destination>
+ <pdf>
+ <fontInfo/>
+ </pdf>
+</agent>
+<present>
+ <pdf>
+ <version>1.7</version>
+ <adobeExtensionLevel>8</adobeExtensionLevel>
+ <renderPolicy>client</renderPolicy>
+ <scriptModel>XFA</scriptModel>
+ <interactive>1</interactive>
+ </pdf>
+ <xdp>
+ <packets>*</packets>
+ </xdp>
+ <destination>pdf</destination>
+ <script>
+ <runScripts>server</runScripts>
+ </script>
+</present>
+<acrobat>
+ <acrobat7>
+ <dynamicRender>required</dynamicRender>
+ </acrobat7>
+ <validate>preSubmit</validate>
+</acrobat>
+</config>
+endstream
+endobj
+5 0 obj <<
+ /Length 1772
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/2.6/">
+ <subform layout="tb" locale="en_US" name="form1" restoreState="auto">
+ <pageSet>
+ <pageArea id="Page1" name="Page1">
+ <contentArea h="10.5in" w="8in" x="0.25in" y="0.25in"/>
+ <medium long="11in" short="8.5in" stock="letter"/>
+ </pageArea>
+ </pageSet>
+ <subform h="10.5in" w="8in" name="subform2">
+ <field name="f1" h="10mm" w="10mm" x="20mm" y="20mm">
+ <ui>
+ <choiceList/>
+ </ui>
+ <items save="1">
+ <text>aaaaaaaaaa</text>
+ </items>
+ </field>
+ <subform name="f4" x="1mm" y="1mm">
+ <occur max="-1"/>
+ <field name="f2" h="350mm" w="200mm">
+ <ui>
+ <choiceList textEntry="1">
+ </choiceList>
+ </ui>
+ <items>
+ <text>Albania</text>
+ <text>Andorra</text>
+ <text>Armenia</text>
+ </items>
+ <event activity="change">
+ <script contentType="application/x-javascript">
+ a += 1;
+ if (a == 2) {
+ c = xfa.resolveNode("xfa.form..f1");
+ xfa.host.setFocus(c);
+ d = xfa.resolveNode("xfa.form..f4");
+ d.instanceManager.addInstance(1);
+ d.instanceManager.removeInstance(0);
+ xfa.host.openList(c);
+ }
+ </script>
+ </event>
+ </field>
+ </subform>
+ </subform>
+ <event activity="docReady">
+ <script contentType="application/x-javascript">
+ a = 0;
+ f2 = xfa.resolveNode("xfa.form..f2");
+ f2.rawValue = "minhtttttt";
+ xfa.host.setFocus(f2);
+ </script>
+ </event>
+ </subform>
+</template>
+endstream
+endobj
+6 0 obj <<
+ /Length 3455
+>>
+stream
+<localeSet xmlns="http://www.xfa.org/schema/xfa-locale-set/2.7/">
+ <locale name="en_US" desc="English (United States)">
+ <calendarSymbols name="gregorian">
+ <monthNames>
+ <month>January</month>
+ <month>February</month>
+ <month>March</month>
+ <month>April</month>
+ <month>May</month>
+ <month>June</month>
+ <month>July</month>
+ <month>August</month>
+ <month>September</month>
+ <month>October</month>
+ <month>November</month>
+ <month>December</month>
+ </monthNames>
+ <monthNames abbr="1">
+ <month>Jan</month>
+ <month>Feb</month>
+ <month>Mar</month>
+ <month>Apr</month>
+ <month>May</month>
+ <month>Jun</month>
+ <month>Jul</month>
+ <month>Aug</month>
+ <month>Sep</month>
+ <month>Oct</month>
+ <month>Nov</month>
+ <month>Dec</month>
+ </monthNames>
+ <dayNames>
+ <day>Sunday</day>
+ <day>Monday</day>
+ <day>Tuesday</day>
+ <day>Wednesday</day>
+ <day>Thursday</day>
+ <day>Friday</day>
+ <day>Saturday</day>
+ </dayNames>
+ <dayNames abbr="1">
+ <day>Sun</day>
+ <day>Mon</day>
+ <day>Tue</day>
+ <day>Wed</day>
+ <day>Thu</day>
+ <day>Fri</day>
+ <day>Sat</day>
+ </dayNames>
+ <meridiemNames>
+ <meridiem>AM</meridiem>
+ <meridiem>PM</meridiem>
+ </meridiemNames>
+ <eraNames>
+ <era>BC</era>
+ <era>AD</era>
+ </eraNames>
+ </calendarSymbols>
+ <datePatterns>
+ <datePattern name="full">EEEE, MMMM D, YYYY</datePattern>
+ <datePattern name="long">MMMM D, YYYY</datePattern>
+ <datePattern name="med">MMM D, YYYY</datePattern>
+ <datePattern name="short">M/D/YY</datePattern>
+ </datePatterns>
+ <timePatterns>
+ <timePattern name="full">h:MM:SS A Z</timePattern>
+ <timePattern name="long">h:MM:SS A Z</timePattern>
+ <timePattern name="med">h:MM:SS A</timePattern>
+ <timePattern name="short">h:MM A</timePattern>
+ </timePatterns>
+ <dateTimeSymbols>GyMdkHmsSEDFwWahKzZ</dateTimeSymbols>
+ <numberPatterns>
+ <numberPattern name="numeric">z,zz9.zzz</numberPattern>
+ <numberPattern name="currency">$z,zz9.99|($z,zz9.99)</numberPattern>
+ <numberPattern name="percent">z,zz9%</numberPattern>
+ </numberPatterns>
+ <numberSymbols>
+ <numberSymbol name="decimal">.</numberSymbol>
+ <numberSymbol name="grouping">,</numberSymbol>
+ <numberSymbol name="percent">%</numberSymbol>
+ <numberSymbol name="minus">-</numberSymbol>
+ <numberSymbol name="zero">0</numberSymbol>
+ </numberSymbols>
+ <currencySymbols>
+ <currencySymbol name="symbol">$</currencySymbol>
+ <currencySymbol name="isoname">USD</currencySymbol>
+ <currencySymbol name="decimal">.</currencySymbol>
+ </currencySymbols>
+ <typefaces>
+ <typeface name="Myriad Pro"/>
+ <typeface name="Minion Pro"/>
+ <typeface name="Courier Std"/>
+ <typeface name="Adobe Pi Std"/>
+ <typeface name="Adobe Hebrew"/>
+ <typeface name="Adobe Arabic"/>
+ <typeface name="Adobe Thai"/>
+ <typeface name="Kozuka Gothic Pro-VI M"/>
+ <typeface name="Kozuka Mincho Pro-VI R"/>
+ <typeface name="Adobe Ming Std L"/>
+ <typeface name="Adobe Song Std L"/>
+ <typeface name="Adobe Myungjo Std M"/>
+ </typefaces>
+ </locale>
+</localeSet>
+endstream
+endobj
+7 0 obj <<
+ /Length 11
+>>
+stream
+</xdp:xdp>
+endstream
+endobj
+8 0 obj <<
+ /Type /Pages
+ /Count 1
+ /Kids [9 0 R]
+>>
+endobj
+9 0 obj <<
+ /Type /Page
+ /Parent 8 0 R
+ /MediaBox [0 0 612 792]
+>>
+endobj
+xref
+0 10
+0000000000 65535 f
+0000000015 00000 n
+0000000199 00000 n
+0000000358 00000 n
+0000000534 00000 n
+0000001228 00000 n
+0000003053 00000 n
+0000006561 00000 n
+0000006623 00000 n
+0000006686 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 10
+>>
+startxref
+6763
+%%EOF
diff --git a/testing/resources/bug_1124998.pdf b/testing/resources/bug_1124998.pdf
new file mode 100644
index 0000000..4368222
--- /dev/null
+++ b/testing/resources/bug_1124998.pdf
Binary files differ
diff --git a/testing/resources/bug_1229106.in b/testing/resources/bug_1229106.in
new file mode 100644
index 0000000..7ffe9bf
--- /dev/null
+++ b/testing/resources/bug_1229106.in
@@ -0,0 +1,68 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 4
+ /Kids [3 0 R 3 0 R 5 0 R 5 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 612 792]
+ /Rotate 90
+ /Contents 4 0 R
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+0 1 -1 0 792 0 cm
+0 0 0 rg
+100 400 150 50 re f
+1 0 0 rg
+0 180 100 50 re f
+0 1 0 rg
+0 742 100 50 re f
+0 0 1 rg
+692 742 100 50 re f
+1 0 1 rg
+692 180 100 50 re f
+Q
+endstream
+endobj
+{{object 5 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 792 612]
+ /Contents 6 0 R
+>>
+endobj
+{{object 6 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+0 0 0 rg
+100 220 150 50 re f
+1 0 0 rg
+0 0 100 50 re f
+0 1 0 rg
+0 562 100 50 re f
+0 0 1 rg
+692 562 100 50 re f
+1 0 1 rg
+692 0 100 50 re f
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_1229106.pdf b/testing/resources/bug_1229106.pdf
new file mode 100644
index 0000000..e0d625c
--- /dev/null
+++ b/testing/resources/bug_1229106.pdf
@@ -0,0 +1,81 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /Count 4
+ /Kids [3 0 R 3 0 R 5 0 R 5 0 R]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 612 792]
+ /Rotate 90
+ /Contents 4 0 R
+>>
+endobj
+4 0 obj <<
+ /Length 163
+>>
+stream
+q
+0 1 -1 0 792 0 cm
+0 0 0 rg
+100 400 150 50 re f
+1 0 0 rg
+0 180 100 50 re f
+0 1 0 rg
+0 742 100 50 re f
+0 0 1 rg
+692 742 100 50 re f
+1 0 1 rg
+692 180 100 50 re f
+Q
+endstream
+endobj
+5 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 792 612]
+ /Contents 6 0 R
+>>
+endobj
+6 0 obj <<
+ /Length 141
+>>
+stream
+q
+0 0 0 rg
+100 220 150 50 re f
+1 0 0 rg
+0 0 100 50 re f
+0 1 0 rg
+0 562 100 50 re f
+0 0 1 rg
+692 562 100 50 re f
+1 0 1 rg
+692 0 100 50 re f
+Q
+endstream
+endobj
+xref
+0 7
+0000000000 65535 f
+0000000015 00000 n
+0000000068 00000 n
+0000000149 00000 n
+0000000257 00000 n
+0000000472 00000 n
+0000000567 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 7
+>>
+startxref
+760
+%%EOF
diff --git a/testing/resources/bug_1296920.in b/testing/resources/bug_1296920.in
new file mode 100644
index 0000000..5c9da58
--- /dev/null
+++ b/testing/resources/bug_1296920.in
@@ -0,0 +1,115 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /StructTreeRoot 6 0 R
+ /MarkInfo <<
+ /Marked true
+ >>
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Contents 4 0 R
+ /MediaBox [0 0 100 100]
+ /Parent 2 0 R
+ /Resources <<
+ /Font <<
+ /F1 5 0 R
+ >>
+ >>
+ /StructParents 0
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+/P <</MCID 1>> BDC
+BT
+/F1 12 Tf
+1 0 0 1 20 50 Tm
+(Hello) Tj
+ET
+EMC
+/P <</MCID 2>> BDC
+BT
+/F1 12 Tf
+1 0 0 1 50 50 Tm
+(World) Tj
+ET
+EMC
+endstream
+endobj
+{{object 5 0}} <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Helvetica
+>>
+endobj
+{{object 6 0}} <<
+ /Type /StructTreeRoot
+ /K [9 0 R]
+ /ParentTree 7 0 R
+>>
+endobj
+{{object 7 0}} <<
+ /Nums [0 8 0 R 1 10 0 R]
+>>
+endobj
+{{object 8 0}}
+[12 0 R 13 0 R]
+endobj
+{{object 9 0}} <<
+ /Type /StructElem
+ /S /Document
+ /P 6 0 R
+ /K [10 0 R]
+>>
+endobj
+{{object 10 0}} <<
+ /Type /StructElem
+ /S /Part
+ /P 9 0 R
+ /K [11 0 R]
+>>
+endobj
+{{object 11 0}} <<
+ /Type /StructElem
+ /S /Div
+ /P 10 0 R
+ /K [12 0 R 13 0 R 14 0 R]
+>>
+endobj
+{{object 12 0}} <<
+ /Type /StructElem
+ /S /P
+ /P 11 0 R
+ /K 1
+ /Pg 3 0 R
+>>
+endobj
+{{object 13 0}} <<
+ /Type /StructElem
+ /S /P
+ /P 11 0 R
+ /K 2
+ /Pg 3 0 R
+>>
+endobj
+{{object 14 0}} <<
+ /Type /StructElem
+ /S /Div
+ /P 11 0 R
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_1296920.pdf b/testing/resources/bug_1296920.pdf
new file mode 100644
index 0000000..92d778c
--- /dev/null
+++ b/testing/resources/bug_1296920.pdf
@@ -0,0 +1,136 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /StructTreeRoot 6 0 R
+ /MarkInfo <<
+ /Marked true
+ >>
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Contents 4 0 R
+ /MediaBox [0 0 100 100]
+ /Parent 2 0 R
+ /Resources <<
+ /Font <<
+ /F1 5 0 R
+ >>
+ >>
+ /StructParents 0
+>>
+endobj
+4 0 obj <<
+ /Length 134
+>>
+stream
+/P <</MCID 1>> BDC
+BT
+/F1 12 Tf
+1 0 0 1 20 50 Tm
+(Hello) Tj
+ET
+EMC
+/P <</MCID 2>> BDC
+BT
+/F1 12 Tf
+1 0 0 1 50 50 Tm
+(World) Tj
+ET
+EMC
+endstream
+endobj
+5 0 obj <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Helvetica
+>>
+endobj
+6 0 obj <<
+ /Type /StructTreeRoot
+ /K [9 0 R]
+ /ParentTree 7 0 R
+>>
+endobj
+7 0 obj <<
+ /Nums [0 8 0 R 1 10 0 R]
+>>
+endobj
+8 0 obj
+[12 0 R 13 0 R]
+endobj
+9 0 obj <<
+ /Type /StructElem
+ /S /Document
+ /P 6 0 R
+ /K [10 0 R]
+>>
+endobj
+10 0 obj <<
+ /Type /StructElem
+ /S /Part
+ /P 9 0 R
+ /K [11 0 R]
+>>
+endobj
+11 0 obj <<
+ /Type /StructElem
+ /S /Div
+ /P 10 0 R
+ /K [12 0 R 13 0 R 14 0 R]
+>>
+endobj
+12 0 obj <<
+ /Type /StructElem
+ /S /P
+ /P 11 0 R
+ /K 1
+ /Pg 3 0 R
+>>
+endobj
+13 0 obj <<
+ /Type /StructElem
+ /S /P
+ /P 11 0 R
+ /K 2
+ /Pg 3 0 R
+>>
+endobj
+14 0 obj <<
+ /Type /StructElem
+ /S /Div
+ /P 11 0 R
+>>
+endobj
+xref
+0 15
+0000000000 65535 f
+0000000015 00000 n
+0000000129 00000 n
+0000000192 00000 n
+0000000363 00000 n
+0000000549 00000 n
+0000000625 00000 n
+0000000703 00000 n
+0000000751 00000 n
+0000000782 00000 n
+0000000863 00000 n
+0000000941 00000 n
+0000001033 00000 n
+0000001114 00000 n
+0000001195 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 15
+>>
+startxref
+1259
+%%EOF
diff --git a/testing/resources/bug_1301.pdf b/testing/resources/bug_1301.pdf
index 285c7f7..8c6e45f 100644
--- a/testing/resources/bug_1301.pdf
+++ b/testing/resources/bug_1301.pdf
@@ -217,7 +217,7 @@
endobj
9 0 obj <<
/Type /Page
- /Parent 2 0 R
+ /Parent 8 0 R
/MediaBox [0 0 612 792]
>>
endobj
diff --git a/testing/resources/bug_1302455.in b/testing/resources/bug_1302455.in
new file mode 100644
index 0000000..1e18f12
--- /dev/null
+++ b/testing/resources/bug_1302455.in
@@ -0,0 +1,75 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /AcroForm <<
+ /DR 4 0 R
+ /Fields [6 0 R 7 0 R]
+ >>
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Kids [3 0 R]
+ /Count 1
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Annots [6 0 R 7 0 R]
+ /MediaBox [0 0 300 300]
+ /Resources 4 0 R
+>>
+endobj
+{{object 4 0}} <<
+ /Font <<
+ /F1 5 0 R
+ >>
+>>
+endobj
+{{object 5 0}} <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Helvetica
+>>
+endobj
+{{object 6 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Tx
+ /AP <<
+ /N 8 0 R
+ >>
+ /F 4
+ /Rect [100 100 200 130]
+ /T (Text Box 1)
+>>
+endobj
+{{object 7 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Tx
+ /AP <<
+ /N 8 0 R
+ >>
+ /F 4
+ /Rect [100 160 200 190]
+ /T (Text Box 2)
+>>
+endobj
+{{object 8 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /BBox [0 0 100 30]
+ {{streamlen}}
+>>
+stream
+1 0 0 rg
+0 0 100 30 re f
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_1302455.pdf b/testing/resources/bug_1302455.pdf
new file mode 100644
index 0000000..a9782c4
--- /dev/null
+++ b/testing/resources/bug_1302455.pdf
@@ -0,0 +1,90 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /AcroForm <<
+ /DR 4 0 R
+ /Fields [6 0 R 7 0 R]
+ >>
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /Kids [3 0 R]
+ /Count 1
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Annots [6 0 R 7 0 R]
+ /MediaBox [0 0 300 300]
+ /Resources 4 0 R
+>>
+endobj
+4 0 obj <<
+ /Font <<
+ /F1 5 0 R
+ >>
+>>
+endobj
+5 0 obj <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Helvetica
+>>
+endobj
+6 0 obj <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Tx
+ /AP <<
+ /N 8 0 R
+ >>
+ /F 4
+ /Rect [100 100 200 130]
+ /T (Text Box 1)
+>>
+endobj
+7 0 obj <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Tx
+ /AP <<
+ /N 8 0 R
+ >>
+ /F 4
+ /Rect [100 160 200 190]
+ /T (Text Box 2)
+>>
+endobj
+8 0 obj <<
+ /Type /XObject
+ /Subtype /Form
+ /BBox [0 0 100 30]
+ /Length 25
+>>
+stream
+1 0 0 rg
+0 0 100 30 re f
+endstream
+endobj
+xref
+0 9
+0000000000 65535 f
+0000000015 00000 n
+0000000128 00000 n
+0000000191 00000 n
+0000000311 00000 n
+0000000362 00000 n
+0000000438 00000 n
+0000000581 00000 n
+0000000724 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 9
+>>
+startxref
+855
+%%EOF
diff --git a/testing/resources/bug_1324189.in b/testing/resources/bug_1324189.in
new file mode 100644
index 0000000..45fceba
--- /dev/null
+++ b/testing/resources/bug_1324189.in
@@ -0,0 +1,16 @@
+{{header}}
+{{object 1 0}} <
+ /Type /Catalog
+>>
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
+{{xref}}
+trailer <<
+ /Root 1 0 R
+ /Prev -200
+ {{trailersize}}
+>>
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_1324189.pdf b/testing/resources/bug_1324189.pdf
new file mode 100644
index 0000000..9652398
--- /dev/null
+++ b/testing/resources/bug_1324189.pdf
@@ -0,0 +1,28 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <
+ /Type /Catalog
+>>
+xref
+0 2
+0000000000 65535 f
+0000000015 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 2
+>>
+startxref
+45
+%%EOF
+xref
+0 2
+0000000000 65535 f
+0000000015 00000 n
+trailer <<
+ /Root 1 0 R
+ /Prev -200
+ /Size 2
+>>
+startxref
+151
+%%EOF
diff --git a/testing/resources/bug_1324503.in b/testing/resources/bug_1324503.in
new file mode 100644
index 0000000..a46d142
--- /dev/null
+++ b/testing/resources/bug_1324503.in
@@ -0,0 +1,16 @@
+{{header}}
+{{object 1 0}} <
+ /Type /Catalog
+>>
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
+{{xref}}
+trailer <<
+ /Root 1 0 R
+ /XRefStm -1
+ {{trailersize}}
+>>
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_1324503.pdf b/testing/resources/bug_1324503.pdf
new file mode 100644
index 0000000..2ce7425
--- /dev/null
+++ b/testing/resources/bug_1324503.pdf
@@ -0,0 +1,28 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <
+ /Type /Catalog
+>>
+xref
+0 2
+0000000000 65535 f
+0000000015 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 2
+>>
+startxref
+45
+%%EOF
+xref
+0 2
+0000000000 65535 f
+0000000015 00000 n
+trailer <<
+ /Root 1 0 R
+ /XRefStm -1
+ /Size 2
+>>
+startxref
+151
+%%EOF
diff --git a/testing/resources/bug_1327884.pdf b/testing/resources/bug_1327884.pdf
new file mode 100644
index 0000000..0e28c06
--- /dev/null
+++ b/testing/resources/bug_1327884.pdf
@@ -0,0 +1,6 @@
+%PDF
+1 0 obj<</Pages 2 0 R/AcroForm<</XFA 30 0 R>>>>2 0 obj<</>
+30 0 obj<<>>stream
+<xdp xmlns="http://ns.adobe.com/xdp/"><con0ig><acrobat><acrobat0></acrobat0></acrobat></con0ig><template><desc e=""use=" .[use="*"]"
+endobj
+trailer<</Root 1 0 R>>
\ No newline at end of file
diff --git a/testing/resources/bug_1328389.in b/testing/resources/bug_1328389.in
new file mode 100644
index 0000000..3370ce7
--- /dev/null
+++ b/testing/resources/bug_1328389.in
@@ -0,0 +1,24 @@
+{{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 2 0 R
+ /MediaBox [0 0 100 100]
+ % https://crbug.com/1328389
+ /Foo /
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_1328389.pdf b/testing/resources/bug_1328389.pdf
new file mode 100644
index 0000000..e45a7a8
--- /dev/null
+++ b/testing/resources/bug_1328389.pdf
@@ -0,0 +1,34 @@
+%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 2 0 R
+ /MediaBox [0 0 100 100]
+ % https://crbug.com/1328389
+ /Foo /
+>>
+endobj
+xref
+0 4
+0000000000 65535 f
+0000000015 00000 n
+0000000068 00000 n
+0000000131 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 4
+>>
+startxref
+247
+%%EOF
diff --git a/testing/resources/bug_1333298.in b/testing/resources/bug_1333298.in
new file mode 100644
index 0000000..5b45ed9
--- /dev/null
+++ b/testing/resources/bug_1333298.in
@@ -0,0 +1,28 @@
+{{header}}
+{{include xfa_catalog_1_0.fragment}}
+{{include xfa_object_2_0.fragment}}
+{{include xfa_preamble_3_0.fragment}}
+{{include xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+ {{streamlen}}
+>>
+stream
+<template>
+ <subform>
+ <desc name="N01" use=" .[N01.use=" .[N01.#use]"]"/>
+ <proto>
+ <proto>
+ <bindItems/>
+ </proto>
+ </proto>
+ </subform>
+</template>
+endstream
+endobj
+{{include xfa_locale_6_0.fragment}}
+{{include xfa_postamble_7_0.fragment}}
+{{include xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_1333298.pdf b/testing/resources/bug_1333298.pdf
new file mode 100644
index 0000000..c003ca9
--- /dev/null
+++ b/testing/resources/bug_1333298.pdf
@@ -0,0 +1,237 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /AcroForm 2 0 R
+ /Extensions <<
+ /ADBE <<
+ /BaseVersion /1.7
+ /ExtensionLevel 8
+ >>
+ >>
+ /NeedsRendering true
+ /Pages 8 0 R
+ /Type /Catalog
+>>
+endobj
+2 0 obj <<
+ /XFA [
+ (preamble)
+ 3 0 R
+ (config)
+ 4 0 R
+ (template)
+ 5 0 R
+ (localeSet)
+ 6 0 R
+ (postamble)
+ 7 0 R
+ ]
+>>
+endobj
+3 0 obj <<
+ /Length 124
+>>
+stream
+<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/" timeStamp="2018-02-23T21:37:11Z" uuid="21482798-7bf0-40a4-bc5d-3cefdccf32b5">
+endstream
+endobj
+4 0 obj <<
+ /Length 642
+>>
+stream
+<config xmlns="http://www.xfa.org/schema/xci/3.0/">
+<agent name="designer">
+ <destination>pdf</destination>
+ <pdf>
+ <fontInfo/>
+ </pdf>
+</agent>
+<present>
+ <pdf>
+ <version>1.7</version>
+ <adobeExtensionLevel>8</adobeExtensionLevel>
+ <renderPolicy>client</renderPolicy>
+ <scriptModel>XFA</scriptModel>
+ <interactive>1</interactive>
+ </pdf>
+ <xdp>
+ <packets>*</packets>
+ </xdp>
+ <destination>pdf</destination>
+ <script>
+ <runScripts>server</runScripts>
+ </script>
+</present>
+<acrobat>
+ <acrobat7>
+ <dynamicRender>required</dynamicRender>
+ </acrobat7>
+ <validate>preSubmit</validate>
+</acrobat>
+</config>
+endstream
+endobj
+5 0 obj <<
+ /Length 189
+>>
+stream
+<template>
+ <subform>
+ <desc name="N01" use=" .[N01.use=" .[N01.#use]"]"/>
+ <proto>
+ <proto>
+ <bindItems/>
+ </proto>
+ </proto>
+ </subform>
+</template>
+endstream
+endobj
+6 0 obj <<
+ /Length 3455
+>>
+stream
+<localeSet xmlns="http://www.xfa.org/schema/xfa-locale-set/2.7/">
+ <locale name="en_US" desc="English (United States)">
+ <calendarSymbols name="gregorian">
+ <monthNames>
+ <month>January</month>
+ <month>February</month>
+ <month>March</month>
+ <month>April</month>
+ <month>May</month>
+ <month>June</month>
+ <month>July</month>
+ <month>August</month>
+ <month>September</month>
+ <month>October</month>
+ <month>November</month>
+ <month>December</month>
+ </monthNames>
+ <monthNames abbr="1">
+ <month>Jan</month>
+ <month>Feb</month>
+ <month>Mar</month>
+ <month>Apr</month>
+ <month>May</month>
+ <month>Jun</month>
+ <month>Jul</month>
+ <month>Aug</month>
+ <month>Sep</month>
+ <month>Oct</month>
+ <month>Nov</month>
+ <month>Dec</month>
+ </monthNames>
+ <dayNames>
+ <day>Sunday</day>
+ <day>Monday</day>
+ <day>Tuesday</day>
+ <day>Wednesday</day>
+ <day>Thursday</day>
+ <day>Friday</day>
+ <day>Saturday</day>
+ </dayNames>
+ <dayNames abbr="1">
+ <day>Sun</day>
+ <day>Mon</day>
+ <day>Tue</day>
+ <day>Wed</day>
+ <day>Thu</day>
+ <day>Fri</day>
+ <day>Sat</day>
+ </dayNames>
+ <meridiemNames>
+ <meridiem>AM</meridiem>
+ <meridiem>PM</meridiem>
+ </meridiemNames>
+ <eraNames>
+ <era>BC</era>
+ <era>AD</era>
+ </eraNames>
+ </calendarSymbols>
+ <datePatterns>
+ <datePattern name="full">EEEE, MMMM D, YYYY</datePattern>
+ <datePattern name="long">MMMM D, YYYY</datePattern>
+ <datePattern name="med">MMM D, YYYY</datePattern>
+ <datePattern name="short">M/D/YY</datePattern>
+ </datePatterns>
+ <timePatterns>
+ <timePattern name="full">h:MM:SS A Z</timePattern>
+ <timePattern name="long">h:MM:SS A Z</timePattern>
+ <timePattern name="med">h:MM:SS A</timePattern>
+ <timePattern name="short">h:MM A</timePattern>
+ </timePatterns>
+ <dateTimeSymbols>GyMdkHmsSEDFwWahKzZ</dateTimeSymbols>
+ <numberPatterns>
+ <numberPattern name="numeric">z,zz9.zzz</numberPattern>
+ <numberPattern name="currency">$z,zz9.99|($z,zz9.99)</numberPattern>
+ <numberPattern name="percent">z,zz9%</numberPattern>
+ </numberPatterns>
+ <numberSymbols>
+ <numberSymbol name="decimal">.</numberSymbol>
+ <numberSymbol name="grouping">,</numberSymbol>
+ <numberSymbol name="percent">%</numberSymbol>
+ <numberSymbol name="minus">-</numberSymbol>
+ <numberSymbol name="zero">0</numberSymbol>
+ </numberSymbols>
+ <currencySymbols>
+ <currencySymbol name="symbol">$</currencySymbol>
+ <currencySymbol name="isoname">USD</currencySymbol>
+ <currencySymbol name="decimal">.</currencySymbol>
+ </currencySymbols>
+ <typefaces>
+ <typeface name="Myriad Pro"/>
+ <typeface name="Minion Pro"/>
+ <typeface name="Courier Std"/>
+ <typeface name="Adobe Pi Std"/>
+ <typeface name="Adobe Hebrew"/>
+ <typeface name="Adobe Arabic"/>
+ <typeface name="Adobe Thai"/>
+ <typeface name="Kozuka Gothic Pro-VI M"/>
+ <typeface name="Kozuka Mincho Pro-VI R"/>
+ <typeface name="Adobe Ming Std L"/>
+ <typeface name="Adobe Song Std L"/>
+ <typeface name="Adobe Myungjo Std M"/>
+ </typefaces>
+ </locale>
+</localeSet>
+endstream
+endobj
+7 0 obj <<
+ /Length 11
+>>
+stream
+</xdp:xdp>
+endstream
+endobj
+8 0 obj <<
+ /Type /Pages
+ /Count 1
+ /Kids [9 0 R]
+>>
+endobj
+9 0 obj <<
+ /Type /Page
+ /Parent 8 0 R
+ /MediaBox [0 0 612 792]
+>>
+endobj
+xref
+0 10
+0000000000 65535 f
+0000000015 00000 n
+0000000199 00000 n
+0000000358 00000 n
+0000000534 00000 n
+0000001228 00000 n
+0000001469 00000 n
+0000004977 00000 n
+0000005039 00000 n
+0000005102 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 10
+>>
+startxref
+5179
+%%EOF
diff --git a/testing/resources/bug_1388_2.pdf b/testing/resources/bug_1388_2.pdf
new file mode 100644
index 0000000..1fee088
--- /dev/null
+++ b/testing/resources/bug_1388_2.pdf
@@ -0,0 +1,80 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /MediaBox [0 0 200 200]
+ /Resources <<
+ /ProcSet [/PDF /Text]
+ /Font <<
+ /TT2 5 0 R
+ >>
+ >>
+>>
+endobj
+4 0 obj <<
+ /Length 40
+>>
+stream
+BT
+/TT2 12 Tf
+40 100 Td
+[(X X)] TJ
+ET
+endstream
+endobj
+5 0 obj <<
+ /Type /Font
+ /Subtype /TrueType
+ /BaseFont /TimesNewRomanPSMT
+ /Encoding /WinAnsiEncoding
+ /FirstChar 31
+ /FontDescriptor 6 0 R
+ /LastChar 252
+>>
+endobj
+6 0 obj <<
+ /Type /FontDescriptor
+ /Ascent 891
+ /CapHeight 656
+ /Descent -216
+ /Flags 34
+ /FontBBox [-568 -307 2000 1007]
+ /FontFamily (Times New Roman)
+ /FontName /TimesNewRomanPSMT
+ /FontStretch /Normal
+ /FontWeight 400
+ /ItalicAngle 0
+ /MissingWidth 778
+ /StemV 82
+ /XHeight -546
+>>
+endobj
+xref
+0 7
+0000000000 65535 f
+0000000015 00000 n
+0000000068 00000 n
+0000000131 00000 n
+0000000310 00000 n
+0000000401 00000 n
+0000000573 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 7
+>>
+startxref
+880
+%%EOF
diff --git a/testing/resources/bug_1396264.in b/testing/resources/bug_1396264.in
new file mode 100644
index 0000000..db1fc5a
--- /dev/null
+++ b/testing/resources/bug_1396264.in
@@ -0,0 +1,86 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /MediaBox [0 0 100 200]
+ /Resources <<
+ /XObject <<
+ % Both images are BGRA.
+ /ImBlue 5 0 R
+ /ImRed 6 0 R
+ >>
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+100 0 0 100 0 0 cm
+/ImBlue Do
+Q
+q
+100 0 0 100 0 100 cm
+/ImRed Do
+Q
+endstream
+endobj
+{{object 5 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 8
+ /ColorSpace /DeviceRGB
+ /Filter [/ASCIIHexDecode /JPXDecode]
+ /Height 64
+ /Width 64
+ {{streamlen}}
+>>
+stream
+0000000c6a5020200d0a870a00000014667479706a703220000000006a7032200000004f6a7032
+68000000166968647200000040000000400004070700000000000f636f6c720100000000001000
+000022636465660004000000000001000100000002000200000003000300010000000000c06a70
+3263ff4fff51003200000000004000000040000000000000000000000040000000400000000000
+0000000004070101070101070101070101ff52000c00000001010504040001ff5c001340404848
+50484850484850484850484850ff640025000143726561746564206279204f70656e4a50454720
+76657273696f6e20322e332e30ff90000a0000000000360001ff93cfb41008908a6fdf801801ca
+bf00cfb40c01cabf0000000000000000000000000000000000000000ffd9
+endstream
+endobj
+{{object 6 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 8
+ /ColorSpace /DeviceRGB
+ /Filter [/ASCIIHexDecode /JPXDecode]
+ /Height 64
+ /Width 64
+ {{streamlen}}
+>>
+stream
+0000000c6a5020200d0a870a00000014667479706a703220000000006a7032200000004f6a7032
+68000000166968647200000040000000400004070700000000000f636f6c720100000000001000
+000022636465660004000000000001000100000002000200000003000300010000000000c06a70
+3263ff4fff51003200000000004000000040000000000000000000000040000000400000000000
+0000000004070101070101070101070101ff52000c00000001010504040001ff5c001340404848
+50484850484850484850484850ff640025000143726561746564206279204f70656e4a50454720
+76657273696f6e20322e332e30ff90000a0000000000360001ff93cfb41008908a6f00df801801
+cabfcfb40c01cabf0000000000000000000000000000000000000000ffd9
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_1396264.pdf b/testing/resources/bug_1396264.pdf
new file mode 100644
index 0000000..9c2bddc
--- /dev/null
+++ b/testing/resources/bug_1396264.pdf
@@ -0,0 +1,99 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /MediaBox [0 0 100 200]
+ /Resources <<
+ /XObject <<
+ % Both images are BGRA.
+ /ImBlue 5 0 R
+ /ImRed 6 0 R
+ >>
+ >>
+>>
+endobj
+4 0 obj <<
+ /Length 69
+>>
+stream
+q
+100 0 0 100 0 0 cm
+/ImBlue Do
+Q
+q
+100 0 0 100 0 100 cm
+/ImRed Do
+Q
+endstream
+endobj
+5 0 obj <<
+ /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 8
+ /ColorSpace /DeviceRGB
+ /Filter [/ASCIIHexDecode /JPXDecode]
+ /Height 64
+ /Width 64
+ /Length 614
+>>
+stream
+0000000c6a5020200d0a870a00000014667479706a703220000000006a7032200000004f6a7032
+68000000166968647200000040000000400004070700000000000f636f6c720100000000001000
+000022636465660004000000000001000100000002000200000003000300010000000000c06a70
+3263ff4fff51003200000000004000000040000000000000000000000040000000400000000000
+0000000004070101070101070101070101ff52000c00000001010504040001ff5c001340404848
+50484850484850484850484850ff640025000143726561746564206279204f70656e4a50454720
+76657273696f6e20322e332e30ff90000a0000000000360001ff93cfb41008908a6fdf801801ca
+bf00cfb40c01cabf0000000000000000000000000000000000000000ffd9
+endstream
+endobj
+6 0 obj <<
+ /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 8
+ /ColorSpace /DeviceRGB
+ /Filter [/ASCIIHexDecode /JPXDecode]
+ /Height 64
+ /Width 64
+ /Length 614
+>>
+stream
+0000000c6a5020200d0a870a00000014667479706a703220000000006a7032200000004f6a7032
+68000000166968647200000040000000400004070700000000000f636f6c720100000000001000
+000022636465660004000000000001000100000002000200000003000300010000000000c06a70
+3263ff4fff51003200000000004000000040000000000000000000000040000000400000000000
+0000000004070101070101070101070101ff52000c00000001010504040001ff5c001340404848
+50484850484850484850484850ff640025000143726561746564206279204f70656e4a50454720
+76657273696f6e20322e332e30ff90000a0000000000360001ff93cfb41008908a6f00df801801
+cabfcfb40c01cabf0000000000000000000000000000000000000000ffd9
+endstream
+endobj
+xref
+0 7
+0000000000 65535 f
+0000000015 00000 n
+0000000068 00000 n
+0000000131 00000 n
+0000000339 00000 n
+0000000459 00000 n
+0000001271 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 7
+>>
+startxref
+2083
+%%EOF
diff --git a/testing/resources/bug_1469.jp2 b/testing/resources/bug_1469.jp2
new file mode 100644
index 0000000..b84a029
--- /dev/null
+++ b/testing/resources/bug_1469.jp2
Binary files differ
diff --git a/testing/resources/bug_1506.in b/testing/resources/bug_1506.in
new file mode 100644
index 0000000..63c4c4d
--- /dev/null
+++ b/testing/resources/bug_1506.in
@@ -0,0 +1,60 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /Names <<
+ /Dests 7 0 R
+ >>
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 4
+ /Kids [
+ 3 0 R
+ 4 0 R
+ ]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Pages
+ /Parent 2 0 R
+ /Count 2
+ /Kids [
+ 5 0 R
+ 5 0 R
+ ]
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Pages
+ /Parent 2 0 R
+ /Count 2
+ /Kids [
+ 5 0 R
+ 6 0 R
+ ]
+>>
+endobj
+{{object 5 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 100 200]
+>>
+endobj
+{{object 6 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 100 300]
+>>
+endobj
+{{object 7 0}} <<
+ /Names [
+ (First) [6 0 R]
+ ]
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_1506.pdf b/testing/resources/bug_1506.pdf
new file mode 100644
index 0000000..7395fe1
--- /dev/null
+++ b/testing/resources/bug_1506.pdf
@@ -0,0 +1,74 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /Names <<
+ /Dests 7 0 R
+ >>
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /Count 4
+ /Kids [
+ 3 0 R
+ 4 0 R
+ ]
+>>
+endobj
+3 0 obj <<
+ /Type /Pages
+ /Parent 2 0 R
+ /Count 2
+ /Kids [
+ 5 0 R
+ 5 0 R
+ ]
+>>
+endobj
+4 0 obj <<
+ /Type /Pages
+ /Parent 2 0 R
+ /Count 2
+ /Kids [
+ 5 0 R
+ 6 0 R
+ ]
+>>
+endobj
+5 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 100 200]
+>>
+endobj
+6 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 100 300]
+>>
+endobj
+7 0 obj <<
+ /Names [
+ (First) [6 0 R]
+ ]
+>>
+endobj
+xref
+0 8
+0000000000 65535 f
+0000000015 00000 n
+0000000102 00000 n
+0000000183 00000 n
+0000000280 00000 n
+0000000377 00000 n
+0000000454 00000 n
+0000000531 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 8
+>>
+startxref
+587
+%%EOF
diff --git a/testing/resources/bug_1549.in b/testing/resources/bug_1549.in
new file mode 100644
index 0000000..18285ca
--- /dev/null
+++ b/testing/resources/bug_1549.in
@@ -0,0 +1,63 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 100 150]
+ /Contents 4 0 R
+ /Resources 5 0 R
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+20 0 0 20 0 100 cm
+/Im1 Do
+Q
+BT
+/Cs1 cs
+1 0 0 0 scn
+30 20 50 40 re f
+ET
+endstream
+endobj
+{{object 5 0}} <<
+ /ColorSpace <<
+ /Cs1 /DeviceCMYK
+ >>
+ /ProcSet [/PDF /ImageB]
+ /XObject <<
+ /Im1 6 0 R
+ >>
+>>
+endobj
+{{object 6 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /Width 1
+ /Height 1
+ /BitsPerComponent 8
+ /ColorSpace /DeviceGray
+ /Filter /ASCIIHexDecode
+ {{streamlen}}
+>>
+stream
+33
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_1549.pdf b/testing/resources/bug_1549.pdf
new file mode 100644
index 0000000..946ba43
--- /dev/null
+++ b/testing/resources/bug_1549.pdf
@@ -0,0 +1,76 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 100 150]
+ /Contents 4 0 R
+ /Resources 5 0 R
+>>
+endobj
+4 0 obj <<
+ /Length 74
+>>
+stream
+q
+20 0 0 20 0 100 cm
+/Im1 Do
+Q
+BT
+/Cs1 cs
+1 0 0 0 scn
+30 20 50 40 re f
+ET
+endstream
+endobj
+5 0 obj <<
+ /ColorSpace <<
+ /Cs1 /DeviceCMYK
+ >>
+ /ProcSet [/PDF /ImageB]
+ /XObject <<
+ /Im1 6 0 R
+ >>
+>>
+endobj
+6 0 obj <<
+ /Type /XObject
+ /Subtype /Image
+ /Width 1
+ /Height 1
+ /BitsPerComponent 8
+ /ColorSpace /DeviceGray
+ /Filter /ASCIIHexDecode
+ /Length 3
+>>
+stream
+33
+endstream
+endobj
+xref
+0 7
+0000000000 65535 f
+0000000015 00000 n
+0000000068 00000 n
+0000000131 00000 n
+0000000245 00000 n
+0000000370 00000 n
+0000000494 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 7
+>>
+startxref
+679
+%%EOF
diff --git a/testing/resources/bug_1558.in b/testing/resources/bug_1558.in
new file mode 100644
index 0000000..439038c
--- /dev/null
+++ b/testing/resources/bug_1558.in
@@ -0,0 +1,54 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /MediaBox [0 0 200 200]
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Resources <<
+ /Font <<
+ /F1 4 0 R
+ >>
+ >>
+ /Contents 5 0 R
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Times-Roman
+>>
+endobj
+{{object 5 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+12 40 100 30 re
+W
+n
+0 g
+BT
+20 100 Td
+/F1 12 Tf
+(Clipped Text) Tj
+0 -50 Td
+/F1 12 Tf
+(Unclipped Text) Tj
+ET
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_1558.pdf b/testing/resources/bug_1558.pdf
new file mode 100644
index 0000000..2feec46
--- /dev/null
+++ b/testing/resources/bug_1558.pdf
@@ -0,0 +1,66 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /MediaBox [0 0 200 200]
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Resources <<
+ /Font <<
+ /F1 4 0 R
+ >>
+ >>
+ /Contents 5 0 R
+>>
+endobj
+4 0 obj <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Times-Roman
+>>
+endobj
+5 0 obj <<
+ /Length 111
+>>
+stream
+q
+12 40 100 30 re
+W
+n
+0 g
+BT
+20 100 Td
+/F1 12 Tf
+(Clipped Text) Tj
+0 -50 Td
+/F1 12 Tf
+(Unclipped Text) Tj
+ET
+Q
+endstream
+endobj
+xref
+0 6
+0000000000 65535 f
+0000000015 00000 n
+0000000068 00000 n
+0000000157 00000 n
+0000000283 00000 n
+0000000361 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 6
+>>
+startxref
+524
+%%EOF
diff --git a/testing/resources/bug_1574.in b/testing/resources/bug_1574.in
new file mode 100644
index 0000000..5d7a08d
--- /dev/null
+++ b/testing/resources/bug_1574.in
@@ -0,0 +1,60 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 200 300]
+ /Resources <<
+ /Font <<
+ /F1 4 0 R
+ >>
+ >>
+ /Contents 5 0 R
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Times-Roman
+>>
+endobj
+{{object 5 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+0 0 50 75 re
+W* n
+1 1 1 rg
+0 150 m
+100 150 l
+100 0 l
+0 0 l
+0 150 l
+h
+f*
+q
+0 0 0 rg
+BT
+20 50 Td
+/F1 12 Tf
+(Text) Tj
+ET
+Q
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_1574.pdf b/testing/resources/bug_1574.pdf
new file mode 100644
index 0000000..df27418
--- /dev/null
+++ b/testing/resources/bug_1574.pdf
@@ -0,0 +1,72 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 200 300]
+ /Resources <<
+ /Font <<
+ /F1 4 0 R
+ >>
+ >>
+ /Contents 5 0 R
+>>
+endobj
+4 0 obj <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Times-Roman
+>>
+endobj
+5 0 obj <<
+ /Length 124
+>>
+stream
+q
+0 0 50 75 re
+W* n
+1 1 1 rg
+0 150 m
+100 150 l
+100 0 l
+0 0 l
+0 150 l
+h
+f*
+q
+0 0 0 rg
+BT
+20 50 Td
+/F1 12 Tf
+(Text) Tj
+ET
+Q
+Q
+endstream
+endobj
+xref
+0 6
+0000000000 65535 f
+0000000015 00000 n
+0000000068 00000 n
+0000000131 00000 n
+0000000283 00000 n
+0000000361 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 6
+>>
+startxref
+537
+%%EOF
diff --git a/testing/resources/bug_1591.in b/testing/resources/bug_1591.in
new file mode 100644
index 0000000..7632e13
--- /dev/null
+++ b/testing/resources/bug_1591.in
@@ -0,0 +1,106 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /MediaBox [0 0 160 400]
+ /Resources <<
+ /ProcSet [/PDF /Text]
+ /Font <<
+ /F1 5 0 R
+ >>
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+0 0 0 rg
+BT
+/F1 1 Tf
+240 0 0 240 50 50 Tm
+0.2 Tw
+0.05 0 Td
+(1) Tj
+0.1 0 Td
+(2) Tj
+0.05 0 Td
+(1) Tj
+ET
+endstream
+endobj
+{{object 5 0}} <<
+ /Type /Font
+ /Subtype /Type3
+ /CharProcs 6 0 R
+ /Encoding 7 0 R
+ /FirstChar 49
+ /FontBBox [-1 -8 28 27]
+ /FontMatrix [0.001 0 0 0.001 0 0]
+ /LastChar 50
+ /Name /F1
+ /Resources <<
+ /ProcSet [/PDF /ImageB]
+ >>
+ /Widths [8 10]
+>>
+endobj
+{{object 6 0}} <<
+ /uniE022 8 0 R
+ /uniE023 9 0 R
+>>
+endobj
+{{object 7 0}} <<
+ /Type /Encoding
+ /Differences [49 /uniE022 /uniE023]
+>>
+endobj
+{{object 8 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+6 0 0 48 6 0 cm
+BI
+/W 6
+/H 48
+/BPC 1
+/IM true
+ID
+xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+EI
+Q
+endstream
+endobj
+{{object 9 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+BI
+/W 4
+/H 4
+/BPC 1
+/IM true
+ID
+xx
+EI
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_1591.pdf b/testing/resources/bug_1591.pdf
new file mode 100644
index 0000000..3ec0a13
--- /dev/null
+++ b/testing/resources/bug_1591.pdf
@@ -0,0 +1,122 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /MediaBox [0 0 160 400]
+ /Resources <<
+ /ProcSet [/PDF /Text]
+ /Font <<
+ /F1 5 0 R
+ >>
+ >>
+>>
+endobj
+4 0 obj <<
+ /Length 102
+>>
+stream
+0 0 0 rg
+BT
+/F1 1 Tf
+240 0 0 240 50 50 Tm
+0.2 Tw
+0.05 0 Td
+(1) Tj
+0.1 0 Td
+(2) Tj
+0.05 0 Td
+(1) Tj
+ET
+endstream
+endobj
+5 0 obj <<
+ /Type /Font
+ /Subtype /Type3
+ /CharProcs 6 0 R
+ /Encoding 7 0 R
+ /FirstChar 49
+ /FontBBox [-1 -8 28 27]
+ /FontMatrix [0.001 0 0 0.001 0 0]
+ /LastChar 50
+ /Name /F1
+ /Resources <<
+ /ProcSet [/PDF /ImageB]
+ >>
+ /Widths [8 10]
+>>
+endobj
+6 0 obj <<
+ /uniE022 8 0 R
+ /uniE023 9 0 R
+>>
+endobj
+7 0 obj <<
+ /Type /Encoding
+ /Differences [49 /uniE022 /uniE023]
+>>
+endobj
+8 0 obj <<
+ /Length 105
+>>
+stream
+q
+6 0 0 48 6 0 cm
+BI
+/W 6
+/H 48
+/BPC 1
+/IM true
+ID
+xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+EI
+Q
+endstream
+endobj
+9 0 obj <<
+ /Length 42
+>>
+stream
+q
+BI
+/W 4
+/H 4
+/BPC 1
+/IM true
+ID
+xx
+EI
+Q
+endstream
+endobj
+xref
+0 10
+0000000000 65535 f
+0000000015 00000 n
+0000000068 00000 n
+0000000131 00000 n
+0000000309 00000 n
+0000000463 00000 n
+0000000724 00000 n
+0000000779 00000 n
+0000000856 00000 n
+0000001013 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 10
+>>
+startxref
+1106
+%%EOF
diff --git a/testing/resources/bug_1646.pdf b/testing/resources/bug_1646.pdf
new file mode 100644
index 0000000..8728c1b
--- /dev/null
+++ b/testing/resources/bug_1646.pdf
@@ -0,0 +1,77 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Pages
+ /Count 1
+ /Kids[ 3 0 R ]
+>>
+endobj
+2 0 obj <<
+ /Type /Catalog
+ /Pages 1 0 R
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 1 0 R
+ /Resources 6 0 R
+ /MediaBox[ 0 0 64 64]
+ /Contents[ 5 0 R ]
+>>
+endobj
+4 0 obj <<
+ /Type /XObject
+ /Subtype /Image
+ /Name /I1
+ /Width 5000
+ /Height 5000
+ /BitsPerComponent 8
+ /ColorSpace /DeviceCMYK
+ /Filter [/ASCIIHexDecode /FlateDecode /DCTDecode]
+ /Decode [1.0 0.0 1.0 0.0 1.0 0.0 1.0 0.0]
+ /Length 734
+>>
+stream
+789cedcd3d4ec2001806e00f680529d4165a643671f112c6901095b09838b01983899bd7e02c9ec
+2c143f8b37802af50cba81770799eef9ddebcc9d7bc355f515c2faf96d1e974e2b2bd68be23bfd8
+3edd3fc436f69acf5844afdbdda795b44907699a24e9b0df3f188c86a35136ccb2713e29c679996
+759312bca6955d7f5e8e8783eabe693aaae9a97a806d3dd74972c4e625dc6a68c5519cd6bd4ede3
+f8edb0adabe26f1bcd47e4c922d6b18955c4793cdf9ddedc9e3dfe9d01000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000000000000000000
+0000000000000000000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000000000000000000000000000000000000000000000000000fc835ef
+3fe03ce2e1eba
+endstream
+endobj
+5 0 obj <<
+/Length 28
+>>
+stream
+q
+64 0 0 64 0 0 cm
+/I1 Do
+Q
+endstream
+endobj
+6 0 obj <<
+ /ProcSet [/PDF /ImageC]
+ /XObject<</I1 4 0 R >>
+>>
+endobj
+xref
+0 7
+0000000000 65535 f
+0000000015 00000 n
+0000000079 00000 n
+0000000132 00000 n
+0000000247 00000 n
+0000001253 00000 n
+0000001330 00000 n
+trailer <<
+ /Size 7
+ /Root 2 0 R
+>>
+startxref
+1402
+%%EOF
diff --git a/testing/resources/bug_1752_truetype_font.fragment b/testing/resources/bug_1752_truetype_font.fragment
new file mode 100644
index 0000000..5dc8ea7
--- /dev/null
+++ b/testing/resources/bug_1752_truetype_font.fragment
@@ -0,0 +1,184 @@
+789ced5b0b7054d779fecf7decae9ebb1292102c4877b968179090848431
+60d95aa45d21211e42d2925df1b056da95b4a0d7689757c60fe23c4cd638
+76d2c44d1c9a382e75e28431579452d9a169ea3ab1f31aa74e26f5b46eea
+f1d86de201c7751c9a3ab0dbfffcf7ac9040c4c4339d691b16eefdbff39f
+73feff3fff77cebde7de0bc000a0104f0a682d2ddd5b3efc93879e00b865
+2d6a176df4f95b6039d4022cf82996dd1b3bb675bd19cc6e07a83f07c0ee
+dad81568ea3fb7e202d65fc2e35bdbba6aea0e157ce1d758f7356cdfdb3f
+121ecf5d9c857512da604dfd07125ad9b79d5900793e2c4b03e383236b72
+4a7201e40480f5cc60383e0e25a0a3ade7b0bf6370f8f0c06b254f3e0890
+df8b012e1b8a8623d26b3ff80bec5b85f56b865091f74b3989e50896970e
+8d240e2d5fabf4a03f1b96970d8ff587d5f50ab6cd6fc572c948f8d0b8d4
+0c7f86ed8f62591b0d8f446b7bbe588709c0b2551a1f8b27c00eff82fe8f
+71fbe313d1f181334ffe3b40ce32b4b91078ae58f6ed2b7f7bfcc777da1b
+7e03e5dc0dc08b2bbe1912722740fabcdaa3fe008b3690c0fc613fdb8397
+3127ea3ff2a2da439666feb249930d4b40a6b2040e980fbbb0ebf7a08f34
+b23a9f3d0c2ad8d447d57ad4979b52fe071890c0264939aaa4a88a2429af
+c27d692f1c4a631f17efb865dbd66de0850db0d18cc1f6a014d4807df95f
+311665937a96670272a4f3e023c75fc7f816e3510fcfabf9703746fcbc12
+c7c383e59fc0dd3c12712c12a368c012a332ce21e03349a1f2c6348f6143
+3a9d7e36335e6681e9a13389c639fb77755ee6fac98a6ab1dab26ea0e5ff
+d29f77e3ce9e5030d0ddd5b9bd63dbd62d9bdb37b5b56e6cf1fb9a9b3678
+1befb8bde1b6f5ebd6debae69655b535d52bab9679dc154bf525aef2d2a2
+02873d3f2f273bcb66b5a88a2c31a8d20cd6eb37e40aada025acfbf570eb
+ca2acd5f3ae45b59e5d75b7a0d2dac192814b7deda4a2a3d6c68bd9ae146
+119ea1ee35bcd872e0aa965eb3a577ba2573680dd0c05de89af1239fae4d
+b19eed41c40ffaf490665c20bc85b0e2a6421e165c2eec4151f16835bfd1
+726028e9efc518d9644e76b3de1ccd5e590593d93908731019cbf4f149b6
+ec0e46405ae65f3f89133c8fbbc591fac311a3637bd0ef73ba5ca195556d
+46beeea32a68269386a5d9b092492dc6438707b4c9aa6f278f4d39a0afb7
+3237a247c2bb82861cc6be49d99f4cde6f14541acb759fb1fcc3af97e2c8
+a34695eef31b95dc6a7be7b49ff62b2e99a15638742df91bc0e1e817cecf
+d68485c652e1f80d706848cd06eb0cbaf8cfd982b94e265b74ad25d99b0c
+4fa58ff4e99a434f4ee6e626c7fd986ee808a289a9f4330f388d966321c3
+d13bc4d687c4d05b3adb8d79db77060da9a2451b0aa306ff36eaaeb54e57
+c1749b8eeb5503a605938319d6781a3b8249cd9f7cc0e772bafc3eac7619
+47b607797a1e98d2a0cf791abc35952143eae535dfced4140778cd914c8d
+a9f4626275e4b8bd2b9834948ab688eec7cc3f10368ef4e12cdbcb09d21d
+46fe45a74b4f161668eb6a42d49647d116896986eac66461af991d70fef0
+2e490715f22f9ae282131db80b0ab5753a9ae176fcbabf57fc3d30548a06
+344c786ba53921ba8386d787c01b16ccf9276b6bb047b817898bf98854a3
+461f378af4a669962939b1ae207511dd8ca26603ef66a29751e3a7f585e9
+ebf59921705bfaf6e0d3509f7e7572b5e6fccb7a580d211f6f5cd28cb3cd
+ed4f0623034679af3382eb6f400b3a5d8637844c87f46034c4a71f6668f9
+ab4e9a24219a33ddc1f62ebd7d7b4f70ad08c4ace0e6940aff5566f4a0d3
+348313d1b055d8b4a0e49443d8d0810aad0581ded48067c35a61c3c38109
+272d9fc04d0d5a903921d31ac330966bfea84fb4e3e55946553ead9a5b33
+d62cbc88769a5b9dae90cbfcadac92b05a138eb1878d27b5355385972bac
+b0e13c6d6e2515cf65299ffc5a508fea217d4833bc1d413e369e1ecab248
+06e55c70d53dab3423599826706175a6c09369b4543a6726d7d84865be6c
+0ca95bc7c982ebea085675188c37d8490bd6897329e49cad479569e9f7f7
+751a6dd7766d339df29e499bdede95e431eb224ec08414e0d1b61753d185
+73b777ae0a9cb2deb0a8d0927a5b24a977051b9c9483cee0ddce0ff31116
+423b6bef6e5a593529b1a6499d1ddd3ee96547bb7a8278256d7ada8137fc
+a3ddc1d312939a7b9b42934bb13ef8b486f725d24a5ccb95bca0f102b7d6
+89051bb5773eed053842b50a29a8dc3fc58074b68c8e41ff9464ea1ca623
+3739f2e24dbf7f4a316bbc99d60aea6ca6ee08e9e837099c2c3e0abe0e4b
+879064bcadf8b5089f2077858692bd21bec8a004d3837f99c1f43b305dfa
+1d934cb2e41ad97ab4c9c8d19bb8be91eb1b4dbd85ebad38355909c3eeb3
+b9c3a5a82dfc9e33e9b8c05319c28b49d2f1c64a6f3acb55af054063bfd2
+d8c31abb57631a2b0b94d7cb81f232662f6b2c3b5526efece80934f66ceb
+917a583010ea08066a83de205e9183ca873a7604b41deced1d6c07eb0e04
+3aba03e3ddacbc9bd5767bbb8f74ffb8fbd56eb5760bdb226d0d6c8cb404
+1a5b4eb5482dac3db0a9a32dd086725bc7d640fd56b615e1e68ef640793b
+b3b737b69f6a97bb3a3a03b775b24ed611d8ded111b87529bb271c290fde
+cd0ec6cbca0fc43794ef8fd79527f0f8d3389b8ab397e36c417d69c08a51
+5be474f9486c41f970cc5dbe2f767bf9de586bf95763acc7cd6cee56b7d4
+ea3eea7ec12ddfab3fa4bfa8cbf3ea0bbd230195c901850f586676b9513e
+25cb91ddabcb0ff5b3bedd7a7978777d79efee15e577e2b107f1d4ee1776
+4b2feffec56e6951c41928a92f0e38eaed01bb7d9b5d2ab7bf6897ecf6b4
+5db2e02626809bf7c018dc0ba7e057a038801d29612a9b620f4f76775556
+b64f59d378dfcbead869b0a34645173f7bb7f71896a306047a76062719fb
+54e8e30f3e084d8bdb8d3a5c36bd8b43ed4684af1f0e8e20702c9e2c81a6
+503c5e59b9279ed85f5959194f54f25fdc04f17822612a78cd7e5193a98e
+9b656a453dafd4a0aab2740f80a50872a00377d2f3613f9d67fd945628e2
+327d7ef6395594be88fbecb330efd267d31752f7a6f6a6cea537a53ef0ae
+d27c1661f5c077c617211f2de7a5ce5c4aa5df4df7a6df9525c865f7a7df
+91bd902fdd92fe35eef3f3b0dde7e021b81f77f5c7e104bc010f4c1b7b1c
+9e8047b1eea3b0775af705f812b6fa347cfc8a4f560c5f049f5a40852afa
+13811fb25cd6c292ec59898f5be2cf1552440de0538215aa2719d4349cb6
+2af75ea89bb4a8af349c962584302973b5cad5a7ad9623971a4e33aeaf2f
+701554b80a5c3e494b2d659f4f0da981f7bee1537e04e2c94175aae7d06a
+1e54794b14ab2d4fceb7672b6a9e140be5e529aa0c4a2134d63716aeaba9
+2c28847505856cfebaba55b52ed925ebac3e8bc91e8b5556761997cf3df5
+8c74db13d2fa54cf899632d713ec3ba9dbd473eff9a421f6cd7f3ed49ff2
+037fe2791eb9fc0e66d50ea5f864d5e7bdc56ab31539162eb42b45396565
+457679896e5de0702cd8177ad9c1d63b986c7730bbea70d8014af78540b6
+2fdc17b263409550da48f194d6dcb967f79dbb775f09aea0feca9f55b5f3
+5c9a525c64b11697b1e2224577cd73d5adb965b5bb922d6026d29f675f61
+6ea67eeae8935f4ebdfbf6e5d414cb3f79fc1b4fbfc33cc6d7cf9c51cf9e
+fae67d4f2ec85e6c7cfab957e4d6d18f1cda7bf97397df3a9afce8ddfc49
+ebeef479e5457c529d0f6e6f916c2fceb2cba50be6412c344fc9b5c442b9
+f3a643a5c856d5b22550e080faba5b8bf399ae41c1eac2a5f575f3ad6e79
+ddf9d43bccf2cef1bfdf7d723cf537a93fff2a6b7cf91727379d5297a79e
+4bbd957a23f5a3f58f2d59c03ec9865e63dd533b8e77107fcff32761cca7
+0d9f7597798bf215c852b20a0ad5bc7d215556f2f785942bc962ebd69939
+610e58ce0a5c1ac80e70e905f59ada93da9f7a281563cfb200bbebd9d485
+d49b6f3f2f754abf4c3d92fa887a36f5c9d409369f95ff8ecf5ac67dcabf
+459f3910f0aeb264673305170e9373f32c59b1d0cb16b6dec2b22ccc0e96
+c72c52a16cb130495263214966d9b110bb86bb3dbb89bc69e256d5f2d88a
+5de621fff6d28bf2aacbf74bdd974f4a87d5b38fa7d63e76f922cc8a230b
+5abc95fca158c128b27378146de8dfd268b9d3326679c8f2658b2507c390
+58a1198719014e68f43ec335ce693de398dd3b455e3b2f3f853e2fbff715
+c1b57a1b72ed84dbbd5abe546493162c70dae4458bc1190b210fb9b905c8
+b8a2ce8f85d479b386397b7ccc55309b7b8e9189ba5b71bc4decbf126f7f
+29753af599b3ec43bf7cf3fb2d7f3795fa5dea9fd84296f727c7527f2b2b
+97eb2bdcec18eb7f83edf8ab1d8f75d3e4f879ea259d9de6592963a3d3cf
+ff35d02b30c31c9d1058c2b5fe2d816558c432ef0b70e6b05502ab789d0b
+086c416e13025ba153d205b641b1f4b8c0595024bd26703668f23a8173a0
+4cfebcc0b9e05336089c07d5caeb02e7c30a35c6df7e28fc9abb87a2e298
+41213c2cb084ad9e125886b5f05d811528648b0556a194dd26b0051cac4f
+602b9c60770b6c8315d20181b360993425703678a5ff1438071ae41e8173
+e1a8fcd702e7c12ee51302e7433732dc0c63300e8761026230084390000d
+eaa01656e159838d583b86fa618862a90d46a11faa116d40cd30cacee95e
+712a455146d1d6013c47b025348f8d1f9e880d0e25b4bada5575dac6b1b1
+c1e1a8d636da5fad6d181ed63a79555ceb8cc6a31307a211ecb015fd25f0
+d0a00bc2e82f8eaab1c498d6151e45c83d0cc27ef41d462fd0191ddc3f1c
+46b0057b8c52cfc3381a33d6116c3388f18de259839578cc617bcbd8e858
+e2f0388634121e8c8d0e6a2bb519ee7e7f303b68a87174c19d6b98b26a4c
+dc6aac884ec46363a3daaaeadad5578ccc3271b5fd1825308c47026d8631
+79511ac004ec43dd180cfc1e32b8d9084adefe30ca3eaa9fa05471bb098a
+334a6d6214693f6912d8de2cefc5944e50db089efbb1c61c539c5348238e
+c5b5b096980847a223e1897ddad8c02c2ac3a3116d247c58eb8b6a13d1c1
+583c119d8846b4d8a8d61f9d488451eedd3f118b4762fd094c4bbcfa46f8
+ca0cef5a86b8c7b92cf868f6f1f9384a698ca2bd9119fd7dd1786c70544b
+44c3a89dd9d8cc4c1fe5ee5ab391b9cd9ad670947d87b5691791992ef8aa
+48a091f5780dabc1b18c91a96a347485c66ad28f60fd381ea3623ad460e7
+44627c7d4d4dff58245a3d4899aeee1f1ba919af414f6335571b3f487faa
+d1d0ece863b3927ac55907b2cdc3d94f5cc7716a9a8b364633238e2dba85
+85d9799ac09643d8f320b60bd352cf047af0e0c1ea11918698491405dc31
+3116d9df9f8877e1fa8ef547e335ddd840e46ea26b68ec607f388e56360b
+d7a36494d3b19f263577c9933e44546cc090c2d8ce2ccdee53859aab1764
+1d2d48d88c8e47e348d5fed14874424b0c45b50de3e17e14a2a64acbacd8
+baeadaebe5363cc379353a98c09cd420893383e099db8c53b719fcb8b6bb
+f0bc3213c48c3c85c979f5d8c460cdb01940bc66735bb37f6b977f250530
+fd363c3d800b7fae5f36dd0f25580a15e0060f2c83e5b0827432de1fb2f1
+da6e4da7717fcae89e05500265d89ad7f27b92223c3080e97b2bb779045e
+07b5fff0045ed923d103755085e730b40c4e44f7c19ee17062142f140ac5
+95265bd9c0b66ce8d47834a851454c0aa2d935a63f05ef68337bcfc4d670
+df813854f50d1f1c80f5788e83b7bf7f641c368fc71371e89cd8373100c1
+03e1c47ebab7aad45bc511570a9ffcce5f85c9aee66b677a4cb57466a2cd
+2a9c4fc348ec5df03138069fa5279d937006cec173f043f829bc82a33f0f
+efc225a6e0734b115bc496b22ab69a35301fdbccbad92e1661c32cc1ee62
+1fc3adcb67d97176829d6467d839f61cfb21fb297b85bd8e23445feca899
+0bf688287f5e94bf2fe4bf9952924c69f98c29ad6b85fcb9296d1f13f267
+a6cc8a08f98e29b38f9832275fc8e3a6cc5d21e4b890c25fde2e215f3265
+7eab90df32a57db5905f37a54313f21121df356541a7908690174d59d820
+e47d42be60ca7922be79dd423e2ae4aba62c720b392ca4a82f12fd8b579b
+792c16f5c58f09993265499d908f9bed4a4ed0572986ab80efbfaecc7366
+d683f955ad0816e1fae173a76a468bb90e45cc23b3673edae4f617ced1ce
+fc92f5fe6d78ab20ecc2750b109973857fd0df4c6fadf0199ce54fc069dc
+2b7f1f7e06afc15bf01eb3b04256c656b035ac896d653d6c804db07bf029
+fd11f6387b8a3dc3becb5e623f676fb28b9224e54b0b25b75427dd216d92
+82d2809490ee931e96be249d949e915e907e26bd2ebd23839c2f2f9257c8
+6be516b95bee93c7e57be463f2a3f2d7e4b3f273f24bf2abf25bf225255b
+2955dc740de2e35f2eae461cc994158e2d8455c256c216c236c256c25984
+6d84b3096711ce219c4d3897700ee13cc2b984f309e711b613ce27ec206c
+275c40d841b8907001e179844d568b08cf235c4cb8887009e162c2f30997
+102e253c9ff002c2a5627670bc80b093f042f13d956327617a32200d3e17
+115e4cb89c7019618d70396117618df012c22ec23a61f3abf252c23ae10a
+c24b09bb095710f61076135e46df7115d2015dc5394b1ec193423c790453
+0a31e5115c29c49547b0a5105b1ec197427c7904630a31e6119c29c49947
+b0a6106b1ec19b42bc7904730a31e711dc29c49d47b0a7107b1ec19f42fc
+7904830a31e8111c2ac4a147b0a8108b1ec1a3423c7a04930a31e9115c2a
+c4a547b0a9109b1ec1a7427c7a04a30a31ea119c2ac4a947b0aa10ab1ec1
+ab42bc7a04b30a31eb11dc2ac4ad47b0ab10bb1ec1af42fc7a04c30a31ec
+111c2bc4b147b0ac10cb1ec1b3423c7b04d312c645f76dba365dffda928b
+635b8277e03adc2635c126dc30eec1add104de5db91d09cfa654845485b4
+086915d226760759624720d1acc920751a59a691751ad96e46fb3f1aed42
+9c87593463ae1fab8ad19a77d2aa1b8eeec62c67d3aed58d7bba357007de
+cb3af17e3970c33e149101791a29d3489d4696697433aa9b51fd7145558a
+ab967f2a5a7add98148caa90ae45ef17c78d58b3095bfcbad6002dd0f1be
+5615c85ccd32489946ea34bae9fda6f79bdeffb8bc97a0652bf03df0dcbe
+2df884605e4f6b710f753dbf0a64aea2ca740419a4fe3ff352848cf1b71f
+da9c3e3257faa5d7d857a63952a63dddb476d3daff656b85b89ef81bc3b2
+396c49b4de3276326faee50fdcab0cd7750e9657c0b5bf9916b2708c0b71
+7c6e7c8eaa83b5b807f441e66d22902500f3dda14a670b9dad7436ff7f41
+169db3df375b32fd3b8bd2eb5e49fed077a33c42f3dd28ccba2facc15d6c
+cf7572957bd548de5f9379476825cd152e9661b6e67ec7aacc8966eec66b
+e9d97a748e1833ed2d94ad7cccc7b539ff43da70f441de3dcb74175d046c
+3ac38cb26d2259a0cc13c400dc37c7f79bb5f4fdc647df6f3ae0ca571736
+03c375309b85a5482c9e80fc7dd18951cc3f40e66deecc5925d1584b690e
+306812f23e60f44e8af35984f9e707ff22a5a6cf43161e38163a67beec58
+684edf789ef87c71ccc8d2ec3979e376f89584c7b662ce8c9b6b3ba35567
+651f5eb9a7f43fb89cf97f80fe1b5550eca6
diff --git a/testing/resources/bug_1768.in b/testing/resources/bug_1768.in
new file mode 100644
index 0000000..3f06f7f
--- /dev/null
+++ b/testing/resources/bug_1768.in
@@ -0,0 +1,317 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /MarkInfo <<
+ /Marked true
+ >>
+ /StructTreeRoot 6 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+ /MediaBox [0 0 612 792]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /Resources <<
+ /ProcSet [/PDF /Text]
+ /Font <<
+ /F1 5 0 R
+ >>
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+q /P << /MCID 0 >> BDC Q
+q
+0 0 612 792 re W n
+BT 8 0 0 8 72 712.75 Tm /F1 1 Tf (AAA) Tj ET EMC
+/P << /MCID 1 >> BDC BT 8 0 0 8 532.8 712.75 Tm /F1 1 Tf (111) Tj ET EMC
+
+0.2835 w 2 J
+72 659.03 332.64 24.56 re S
+72 622.98 332.64 36.06 re S
+404.64 622.98 85.68 36.06 re S
+404.64 659.03 85.68 24.56 re S
+490.32 622.98 85.68 36.06 re S
+490.32 659.03 85.68 24.56 re S
+
+/P << /MCID 2 >> BDC BT 10 0 0 10 257.37 667.84 Tm /F1 1 Tf (2) Tj ET EMC
+/P << /MCID 3 >> BDC BT 10 0 0 10 428.73 667.84 Tm /F1 1 Tf (3) Tj ET EMC
+/P << /MCID 4 >> BDC BT 10 0 0 10 514.41 667.84 Tm /F1 1 Tf (4) Tj ET EMC
+/P << /MCID 5 >> BDC BT 10 0 0 10 259.87 637.54 Tm /F1 1 Tf (BB) Tj ET EMC
+/P << /MCID 6 >> BDC BT 10 0 0 10 431.23 637.54 Tm /F1 1 Tf (CC) Tj ET EMC
+/P << /MCID 7 >> BDC BT 10 0 0 10 528.16 637.54 Tm /F1 1 Tf (DD) Tj ET EMC
+Q
+endstream
+endobj
+{{object 5 0}} <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Times-Roman
+>>
+endobj
+{{object 6 0}} <<
+ /Type /StructTreeRoot
+ /K 7 0 R
+>>
+endobj
+{{object 7 0}} <<
+ /Type /StructElem
+ /S /Document
+ /P 6 0 R
+ /K [8 0 R]
+>>
+endobj
+{{object 8 0}} <<
+ /Type /StructElem
+ /S /Document
+ /P 7 0 R
+ /K [9 0 R 10 0 R]
+>>
+endobj
+{{object 9 0}} <<
+ /Type /StructElem
+ /S /Table
+ /P 8 0 R
+ /K [11 0 R]
+>>
+endobj
+{{object 10 0}} <<
+ /Type /StructElem
+ /S /Table
+ /P 8 0 R
+ /K [21 0 R 22 0 R]
+>>
+endobj
+{{object 11 0}} <<
+ /Type /StructElem
+ /S /TR
+ /P 9 0 R
+ /K [12 0 R]
+>>
+endobj
+{{object 12 0}} <<
+ /Type /StructElem
+ /S /TD
+ /P 11 0 R
+ /K [13 0 R]
+>>
+endobj
+{{object 13 0}} <<
+ /Type /StructElem
+ /S /Table
+ /P 12 0 R
+ /K [14 0 R]
+>>
+endobj
+{{object 14 0}} <<
+ /Type /StructElem
+ /S /TR
+ /P 13 0 R
+ /K [15 0 R 16 0 R]
+>>
+endobj
+{{object 15 0}} <<
+ /Type /StructElem
+ /S /TD
+ /P 14 0 R
+ /K [17 0 R]
+>>
+endobj
+{{object 16 0}} <<
+ /Type /StructElem
+ /S /TD
+ /P 14 0 R
+ /K [19 0 R]
+>>
+endobj
+{{object 17 0}} <<
+ /Type /StructElem
+ /S /P
+ /P 15 0 R
+ /K [18 0 R]
+>>
+endobj
+{{object 18 0}} <<
+ /Type /StructElem
+ /S /Span
+ /Pg 3 0 R
+ /P 17 0 R
+ /K 0
+>>
+endobj
+{{object 19 0}} <<
+ /Type /StructElem
+ /S /P
+ /P 16 0 R
+ /K [20 0 R]
+>>
+endobj
+{{object 20 0}} <<
+ /Type /StructElem
+ /S /Span
+ /Pg 3 0 R
+ /P 19 0 R
+ /K 1
+>>
+endobj
+{{object 21 0}} <<
+ /Type /StructElem
+ /S /TR
+ /P 10 0 R
+ /K [23 0 R 24 0 R 25 0 R]
+>>
+endobj
+{{object 22 0}} <<
+ /Type /StructElem
+ /S /TR
+ /P 10 0 R
+ /K [32 0 R 33 0 R 34 0 R]
+>>
+endobj
+{{object 23 0}} <<
+ /Type /StructElem
+ /S /TD
+ /P 21 0 R
+ /K [26 0 R]
+>>
+endobj
+{{object 24 0}} <<
+ /Type /StructElem
+ /S /TD
+ /P 21 0 R
+ /K [28 0 R]
+>>
+endobj
+{{object 25 0}} <<
+ /Type /StructElem
+ /S /TD
+ /P 21 0 R
+ /K [30 0 R]
+>>
+endobj
+{{object 26 0}} <<
+ /Type /StructElem
+ /S /P
+ /P 23 0 R
+ /K [27 0 R]
+>>
+endobj
+{{object 27 0}} <<
+ /Type /StructElem
+ /S /Span
+ /Pg 3 0 R
+ /P 26 0 R
+ /K 2
+>>
+endobj
+{{object 28 0}} <<
+ /Type /StructElem
+ /S /P
+ /P 24 0 R
+ /K [29 0 R]
+>>
+endobj
+{{object 29 0}} <<
+ /Type /StructElem
+ /S /Span
+ /Pg 3 0 R
+ /P 28 0 R
+ /K 3
+>>
+endobj
+{{object 30 0}} <<
+ /Type /StructElem
+ /S /P
+ /P 25 0 R
+ /K [31 0 R]
+>>
+endobj
+{{object 31 0}} <<
+ /Type /StructElem
+ /S /Span
+ /Pg 3 0 R
+ /P 30 0 R
+ /K 4
+>>
+endobj
+{{object 32 0}} <<
+ /Type /StructElem
+ /S /TD
+ /P 22 0 R
+ /K [35 0 R]
+>>
+endobj
+{{object 33 0}} <<
+ /Type /StructElem
+ /S /TD
+ /P 22 0 R
+ /K [37 0 R]
+>>
+endobj
+{{object 34 0}} <<
+ /Type /StructElem
+ /S /TD
+ /P 22 0 R
+ /K [39 0 R]
+>>
+endobj
+{{object 35 0}} <<
+ /Type /StructElem
+ /S /P
+ /P 32 0 R
+ /K [36 0 R]
+>>
+endobj
+{{object 36 0}} <<
+ /Type /StructElem
+ /S /Span
+ /Pg 3 0 R
+ /P 35 0 R
+ /K 5
+>>
+endobj
+{{object 37 0}} <<
+ /Type /StructElem
+ /S /P
+ /P 33 0 R
+ /K [38 0 R]
+>>
+endobj
+{{object 38 0}} <<
+ /Type /StructElem
+ /S /Span
+ /Pg 3 0 R
+ /P 37 0 R
+ /K 6
+>>
+endobj
+{{object 39 0}} <<
+ /Type /StructElem
+ /S /P
+ /P 34 0 R
+ /K [40 0 R]
+>>
+endobj
+{{object 40 0}} <<
+ /Type /StructElem
+ /S /Span
+ /Pg 3 0 R
+ /P 39 0 R
+ /K 7
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_1768.pdf b/testing/resources/bug_1768.pdf
new file mode 100644
index 0000000..32b1644
--- /dev/null
+++ b/testing/resources/bug_1768.pdf
@@ -0,0 +1,364 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /MarkInfo <<
+ /Marked true
+ >>
+ /StructTreeRoot 6 0 R
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+ /MediaBox [0 0 612 792]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /Resources <<
+ /ProcSet [/PDF /Text]
+ /Font <<
+ /F1 5 0 R
+ >>
+ >>
+>>
+endobj
+4 0 obj <<
+ /Length 812
+>>
+stream
+q /P << /MCID 0 >> BDC Q
+q
+0 0 612 792 re W n
+BT 8 0 0 8 72 712.75 Tm /F1 1 Tf (AAA) Tj ET EMC
+/P << /MCID 1 >> BDC BT 8 0 0 8 532.8 712.75 Tm /F1 1 Tf (111) Tj ET EMC
+
+0.2835 w 2 J
+72 659.03 332.64 24.56 re S
+72 622.98 332.64 36.06 re S
+404.64 622.98 85.68 36.06 re S
+404.64 659.03 85.68 24.56 re S
+490.32 622.98 85.68 36.06 re S
+490.32 659.03 85.68 24.56 re S
+
+/P << /MCID 2 >> BDC BT 10 0 0 10 257.37 667.84 Tm /F1 1 Tf (2) Tj ET EMC
+/P << /MCID 3 >> BDC BT 10 0 0 10 428.73 667.84 Tm /F1 1 Tf (3) Tj ET EMC
+/P << /MCID 4 >> BDC BT 10 0 0 10 514.41 667.84 Tm /F1 1 Tf (4) Tj ET EMC
+/P << /MCID 5 >> BDC BT 10 0 0 10 259.87 637.54 Tm /F1 1 Tf (BB) Tj ET EMC
+/P << /MCID 6 >> BDC BT 10 0 0 10 431.23 637.54 Tm /F1 1 Tf (CC) Tj ET EMC
+/P << /MCID 7 >> BDC BT 10 0 0 10 528.16 637.54 Tm /F1 1 Tf (DD) Tj ET EMC
+Q
+endstream
+endobj
+5 0 obj <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Times-Roman
+>>
+endobj
+6 0 obj <<
+ /Type /StructTreeRoot
+ /K 7 0 R
+>>
+endobj
+7 0 obj <<
+ /Type /StructElem
+ /S /Document
+ /P 6 0 R
+ /K [8 0 R]
+>>
+endobj
+8 0 obj <<
+ /Type /StructElem
+ /S /Document
+ /P 7 0 R
+ /K [9 0 R 10 0 R]
+>>
+endobj
+9 0 obj <<
+ /Type /StructElem
+ /S /Table
+ /P 8 0 R
+ /K [11 0 R]
+>>
+endobj
+10 0 obj <<
+ /Type /StructElem
+ /S /Table
+ /P 8 0 R
+ /K [21 0 R 22 0 R]
+>>
+endobj
+11 0 obj <<
+ /Type /StructElem
+ /S /TR
+ /P 9 0 R
+ /K [12 0 R]
+>>
+endobj
+12 0 obj <<
+ /Type /StructElem
+ /S /TD
+ /P 11 0 R
+ /K [13 0 R]
+>>
+endobj
+13 0 obj <<
+ /Type /StructElem
+ /S /Table
+ /P 12 0 R
+ /K [14 0 R]
+>>
+endobj
+14 0 obj <<
+ /Type /StructElem
+ /S /TR
+ /P 13 0 R
+ /K [15 0 R 16 0 R]
+>>
+endobj
+15 0 obj <<
+ /Type /StructElem
+ /S /TD
+ /P 14 0 R
+ /K [17 0 R]
+>>
+endobj
+16 0 obj <<
+ /Type /StructElem
+ /S /TD
+ /P 14 0 R
+ /K [19 0 R]
+>>
+endobj
+17 0 obj <<
+ /Type /StructElem
+ /S /P
+ /P 15 0 R
+ /K [18 0 R]
+>>
+endobj
+18 0 obj <<
+ /Type /StructElem
+ /S /Span
+ /Pg 3 0 R
+ /P 17 0 R
+ /K 0
+>>
+endobj
+19 0 obj <<
+ /Type /StructElem
+ /S /P
+ /P 16 0 R
+ /K [20 0 R]
+>>
+endobj
+20 0 obj <<
+ /Type /StructElem
+ /S /Span
+ /Pg 3 0 R
+ /P 19 0 R
+ /K 1
+>>
+endobj
+21 0 obj <<
+ /Type /StructElem
+ /S /TR
+ /P 10 0 R
+ /K [23 0 R 24 0 R 25 0 R]
+>>
+endobj
+22 0 obj <<
+ /Type /StructElem
+ /S /TR
+ /P 10 0 R
+ /K [32 0 R 33 0 R 34 0 R]
+>>
+endobj
+23 0 obj <<
+ /Type /StructElem
+ /S /TD
+ /P 21 0 R
+ /K [26 0 R]
+>>
+endobj
+24 0 obj <<
+ /Type /StructElem
+ /S /TD
+ /P 21 0 R
+ /K [28 0 R]
+>>
+endobj
+25 0 obj <<
+ /Type /StructElem
+ /S /TD
+ /P 21 0 R
+ /K [30 0 R]
+>>
+endobj
+26 0 obj <<
+ /Type /StructElem
+ /S /P
+ /P 23 0 R
+ /K [27 0 R]
+>>
+endobj
+27 0 obj <<
+ /Type /StructElem
+ /S /Span
+ /Pg 3 0 R
+ /P 26 0 R
+ /K 2
+>>
+endobj
+28 0 obj <<
+ /Type /StructElem
+ /S /P
+ /P 24 0 R
+ /K [29 0 R]
+>>
+endobj
+29 0 obj <<
+ /Type /StructElem
+ /S /Span
+ /Pg 3 0 R
+ /P 28 0 R
+ /K 3
+>>
+endobj
+30 0 obj <<
+ /Type /StructElem
+ /S /P
+ /P 25 0 R
+ /K [31 0 R]
+>>
+endobj
+31 0 obj <<
+ /Type /StructElem
+ /S /Span
+ /Pg 3 0 R
+ /P 30 0 R
+ /K 4
+>>
+endobj
+32 0 obj <<
+ /Type /StructElem
+ /S /TD
+ /P 22 0 R
+ /K [35 0 R]
+>>
+endobj
+33 0 obj <<
+ /Type /StructElem
+ /S /TD
+ /P 22 0 R
+ /K [37 0 R]
+>>
+endobj
+34 0 obj <<
+ /Type /StructElem
+ /S /TD
+ /P 22 0 R
+ /K [39 0 R]
+>>
+endobj
+35 0 obj <<
+ /Type /StructElem
+ /S /P
+ /P 32 0 R
+ /K [36 0 R]
+>>
+endobj
+36 0 obj <<
+ /Type /StructElem
+ /S /Span
+ /Pg 3 0 R
+ /P 35 0 R
+ /K 5
+>>
+endobj
+37 0 obj <<
+ /Type /StructElem
+ /S /P
+ /P 33 0 R
+ /K [38 0 R]
+>>
+endobj
+38 0 obj <<
+ /Type /StructElem
+ /S /Span
+ /Pg 3 0 R
+ /P 37 0 R
+ /K 6
+>>
+endobj
+39 0 obj <<
+ /Type /StructElem
+ /S /P
+ /P 34 0 R
+ /K [40 0 R]
+>>
+endobj
+40 0 obj <<
+ /Type /StructElem
+ /S /Span
+ /Pg 3 0 R
+ /P 39 0 R
+ /K 7
+>>
+endobj
+xref
+0 41
+0000000000 65535 f
+0000000015 00000 n
+0000000129 00000 n
+0000000218 00000 n
+0000000370 00000 n
+0000001234 00000 n
+0000001312 00000 n
+0000001368 00000 n
+0000001448 00000 n
+0000001535 00000 n
+0000001613 00000 n
+0000001699 00000 n
+0000001775 00000 n
+0000001852 00000 n
+0000001932 00000 n
+0000002016 00000 n
+0000002093 00000 n
+0000002170 00000 n
+0000002246 00000 n
+0000002330 00000 n
+0000002406 00000 n
+0000002490 00000 n
+0000002581 00000 n
+0000002672 00000 n
+0000002749 00000 n
+0000002826 00000 n
+0000002903 00000 n
+0000002979 00000 n
+0000003063 00000 n
+0000003139 00000 n
+0000003223 00000 n
+0000003299 00000 n
+0000003383 00000 n
+0000003460 00000 n
+0000003537 00000 n
+0000003614 00000 n
+0000003690 00000 n
+0000003774 00000 n
+0000003850 00000 n
+0000003934 00000 n
+0000004010 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 41
+>>
+startxref
+4094
+%%EOF
diff --git a/testing/resources/bug_1769.in b/testing/resources/bug_1769.in
new file mode 100644
index 0000000..a6f6682
--- /dev/null
+++ b/testing/resources/bug_1769.in
@@ -0,0 +1,72 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /MediaBox [0 0 612 792]
+ /Resources <<
+ /XObject <<
+ /Im1 5 0 R
+ >>
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+1000 0 0 1000 50 100 cm
+/Im1 Do
+Q
+q
+1 0 0 1 200 100 cm
+/Im1 Do
+Q
+endstream
+endobj
+{{object 5 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /BBox [0 0 100 100]
+ /Resources <<
+ /Font <<
+ /F1 6 0 R
+ >>
+ >>
+{{streamlen}}
+>>
+stream
+0.001 0 0 0.001 0 0 cm
+BT
+/F1 24 Tf
+(w) Tj
+(o) Tj
+(r) Tj
+(l) Tj
+(d) Tj
+ET
+endstream
+endobj
+{{object 6 0}} <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Helvetica
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_1769.pdf b/testing/resources/bug_1769.pdf
new file mode 100644
index 0000000..3a9fba1
--- /dev/null
+++ b/testing/resources/bug_1769.pdf
@@ -0,0 +1,85 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /MediaBox [0 0 612 792]
+ /Resources <<
+ /XObject <<
+ /Im1 5 0 R
+ >>
+ >>
+>>
+endobj
+4 0 obj <<
+ /Length 67
+>>
+stream
+q
+1000 0 0 1000 50 100 cm
+/Im1 Do
+Q
+q
+1 0 0 1 200 100 cm
+/Im1 Do
+Q
+endstream
+endobj
+5 0 obj <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /BBox [0 0 100 100]
+ /Resources <<
+ /Font <<
+ /F1 6 0 R
+ >>
+ >>
+/Length 74
+>>
+stream
+0.001 0 0 0.001 0 0 cm
+BT
+/F1 24 Tf
+(w) Tj
+(o) Tj
+(r) Tj
+(l) Tj
+(d) Tj
+ET
+endstream
+endobj
+6 0 obj <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Helvetica
+>>
+endobj
+xref
+0 7
+0000000000 65535 f
+0000000015 00000 n
+0000000068 00000 n
+0000000131 00000 n
+0000000287 00000 n
+0000000405 00000 n
+0000000655 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 7
+>>
+startxref
+731
+%%EOF
diff --git a/testing/resources/bug_1919.pdf b/testing/resources/bug_1919.pdf
new file mode 100644
index 0000000..8331e0d
--- /dev/null
+++ b/testing/resources/bug_1919.pdf
Binary files differ
diff --git a/testing/resources/bug_306123.pdf b/testing/resources/bug_306123.pdf
index 5bd1900..8b542cc 100644
--- a/testing/resources/bug_306123.pdf
+++ b/testing/resources/bug_306123.pdf
@@ -1243,7 +1243,7 @@
endobj
9 0 obj <<
/Type /Page
- /Parent 2 0 R
+ /Parent 8 0 R
/MediaBox [0 0 612 792]
>>
endobj
diff --git a/testing/resources/bug_481363.in b/testing/resources/bug_481363.in
index 32a724d..1e05346 100644
--- a/testing/resources/bug_481363.in
+++ b/testing/resources/bug_481363.in
@@ -29,7 +29,7 @@
100 500 100 100 re b
endstream
endobj
-{{object 5 0)) <<
+{{object 5 0}} <<
/Type /Font
/Subtype /Type1
/BaseFont /He
diff --git a/testing/resources/bug_481363.pdf b/testing/resources/bug_481363.pdf
index 53468a0..8263b3f 100644
--- a/testing/resources/bug_481363.pdf
+++ b/testing/resources/bug_481363.pdf
@@ -30,7 +30,7 @@
100 500 100 100 re b
endstream
endobj
-{{object 5 0)) <<
+5 0 obj <<
/Type /Font
/Subtype /Type1
/BaseFont /He
@@ -51,12 +51,12 @@
0000000078 00000 n
0000000253 00000 n
0000000306 00000 n
-0000000000 65535 f
-0000000517 00000 n
+0000000400 00000 n
+0000000510 00000 n
trailer <<
/Size 0
/Root 3 0 R
>>
startxref
-621
+614
%%EOF
diff --git a/testing/resources/bug_674771.in b/testing/resources/bug_674771.in
new file mode 100644
index 0000000..3352f30
--- /dev/null
+++ b/testing/resources/bug_674771.in
@@ -0,0 +1,56 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents [4 0 R]
+ /MediaBox [0 0 792 612]
+ /Resources <<
+ /ProcSet [/PDF /ImageB]
+ /XObject <<
+ /Im0 5 0 R
+ >>
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+400 0 0 300 60 100 cm
+/Im0 Do
+Q
+endstream
+endobj
+{{object 5 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 1
+ /Decode [0.0 1.0]
+ /Filter [/ASCIIHexDecode /JBIG2Decode]
+ /Height 400
+ /ImageMask true
+ /Width 400
+ {{streamlen}}
+>>
+stream
+000000003000010000001300000dea00000353000017110000171151000000000001260001000000
+3500000dea000003530000000000000000020003fffdff02fefefeff7f86530fb6c922cfff7fff7f
+ff7fff7fff7fff7fff7fff7fffac
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_674771.pdf b/testing/resources/bug_674771.pdf
new file mode 100644
index 0000000..6b2dab8
--- /dev/null
+++ b/testing/resources/bug_674771.pdf
@@ -0,0 +1,68 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents [4 0 R]
+ /MediaBox [0 0 792 612]
+ /Resources <<
+ /ProcSet [/PDF /ImageB]
+ /XObject <<
+ /Im0 5 0 R
+ >>
+ >>
+>>
+endobj
+4 0 obj <<
+ /Length 34
+>>
+stream
+q
+400 0 0 300 60 100 cm
+/Im0 Do
+Q
+endstream
+endobj
+5 0 obj <<
+ /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 1
+ /Decode [0.0 1.0]
+ /Filter [/ASCIIHexDecode /JBIG2Decode]
+ /Height 400
+ /ImageMask true
+ /Width 400
+ /Length 191
+>>
+stream
+000000003000010000001300000dea00000353000017110000171151000000000001260001000000
+3500000dea000003530000000000000000020003fffdff02fefefeff7f86530fb6c922cfff7fff7f
+ff7fff7fff7fff7fff7fff7fffac
+endstream
+endobj
+xref
+0 6
+0000000000 65535 f
+0000000015 00000 n
+0000000068 00000 n
+0000000131 00000 n
+0000000317 00000 n
+0000000402 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 6
+>>
+startxref
+808
+%%EOF
diff --git a/testing/resources/bug_680376.in b/testing/resources/bug_680376.in
index c21df24..32daee1 100644
--- a/testing/resources/bug_680376.in
+++ b/testing/resources/bug_680376.in
@@ -85,8 +85,8 @@
>>
endobj
% Old-style top-level Dests dictionary. Note that FirstAlternate
-% intentionally references non-exisstant page 11 and LastAlternate
-% intentionally references non-existant object 999.
+% intentionally references non-existent page 11 and LastAlternate
+% intentionally references non-existent object 999.
{{object 14 0}} <<
/FirstAlternate [11 /XYZ 200 400 800]
/LastAlternate <</D [999 0 R /XYZ 0 0 -200]>>
diff --git a/testing/resources/bug_680376.pdf b/testing/resources/bug_680376.pdf
index fb01c57..8d7a884 100644
--- a/testing/resources/bug_680376.pdf
+++ b/testing/resources/bug_680376.pdf
@@ -1,5 +1,5 @@
%PDF-1.7
-% ò¤ô
+% ò¤ô
1 0 obj <<
/Type /Catalog
/Pages 2 0 R
@@ -86,8 +86,8 @@
>>
endobj
% Old-style top-level Dests dictionary. Note that FirstAlternate
-% intentionally references non-exisstant page 11 and LastAlternate
-% intentionally references non-existant object 999.
+% intentionally references non-existent page 11 and LastAlternate
+% intentionally references non-existent object 999.
14 0 obj <<
/FirstAlternate [11 /XYZ 200 400 800]
/LastAlternate <</D [999 0 R /XYZ 0 0 -200]>>
@@ -138,19 +138,19 @@
0000000903 00000 n
0000000993 00000 n
0000000000 65535 f
-0000001287 00000 n
-0000001415 00000 n
+0000001286 00000 n
+0000001414 00000 n
0000000000 65535 f
0000000000 65535 f
0000000000 65535 f
0000000000 65535 f
0000000000 65535 f
-0000001510 00000 n
-0000001620 00000 n
+0000001509 00000 n
+0000001619 00000 n
trailer <<
/Size 6
/Root 1 0 R
>>
startxref
-1708
+1707
%%EOF
diff --git a/testing/resources/bug_765384.in b/testing/resources/bug_765384.in
index e7e46c7..69256bc 100644
--- a/testing/resources/bug_765384.in
+++ b/testing/resources/bug_765384.in
@@ -66,7 +66,7 @@
/JS 21 0 R
>>
endobj
-% JS program to exexute
+% JS program to execute
{{object 21 0}} <<
>>
stream
diff --git a/testing/resources/bug_765384.pdf b/testing/resources/bug_765384.pdf
index e820fcc..41e042d 100644
--- a/testing/resources/bug_765384.pdf
+++ b/testing/resources/bug_765384.pdf
@@ -67,7 +67,7 @@
/JS 21 0 R
>>
endobj
-% JS program to exexute
+% JS program to execute
21 0 obj <<
>>
stream
diff --git a/testing/resources/bug_821454.in b/testing/resources/bug_821454.in
index 168c983..ee7f20c 100644
--- a/testing/resources/bug_821454.in
+++ b/testing/resources/bug_821454.in
@@ -1,71 +1,63 @@
{{header}}
-{{object 1 0}}
-<<
+{{object 1 0}} <<
/Type /Catalog
/Pages 2 0 R
- /Names 14 0 R
- /AcroForm << /Fields [ 8 0 R 9 0 R ] >>
+ /Names 7 0 R
+ /AcroForm <<
+ /Fields [ 5 0 R 6 0 R ]
+ >>
>>
endobj
-{{object 2 0}}
-<<
+{{object 2 0}} <<
/Type /Pages
/Count 1
/Kids [ 3 0 R ]
>>
endobj
-{{object 3 0}}
-<<
+{{object 3 0}} <<
/Type /Page
/Parent 2 0 R
- /Contents 6 0 R
+ /Contents 4 0 R
/MediaBox [ 0 0 300 600 ]
- /Annots [ 8 0 R 9 0 R ]
+ /Annots [ 5 0 R 6 0 R ]
>>
endobj
-{{object 6 0}}
-<<
-{{streamlen}}
+{{object 4 0}} <<
+ {{streamlen}}
>>
stream
endstream
endobj
-{{object 8 0}}
-<<
+{{object 5 0}} <<
/Type /Annot
/Subtype /Link
/Rect [ 100 350 200 380 ]
/Dest (target10)
>>
endobj
-{{object 9 0}}
-<<
+{{object 6 0}} <<
/Type /Annot
/Subtype /Link
/Rect [ 100 400 200 430 ]
/Dest (target100)
>>
endobj
-{{object 14 0}}
-<<
- /Dests 15 0 R
+{{object 7 0}} <<
+ /Dests 8 0 R
>>
endobj
-{{object 15 0}}
-<<
+{{object 8 0}} <<
/Names [
- (target10) 16 0 R
- (target100) 17 0 R
+ (target10) 9 0 R
+ (target100) 10 0 R
]
>>
endobj
-{{object 16 0}}
-<<
+{{object 9 0}} <<
/D [3 0 R /XYZ 100 200 0]
>>
endobj
-{{object 17 0}}
-<<
+{{object 10 0}} <<
/D [3 0 R /XYZ 150 250 0]
>>
endobj
diff --git a/testing/resources/bug_821454.pdf b/testing/resources/bug_821454.pdf
index d6229a5..1e11c13 100644
--- a/testing/resources/bug_821454.pdf
+++ b/testing/resources/bug_821454.pdf
@@ -1,96 +1,84 @@
%PDF-1.7
% ò¤ô
-1 0 obj
-<<
+1 0 obj <<
/Type /Catalog
/Pages 2 0 R
- /Names 14 0 R
- /AcroForm << /Fields [ 8 0 R 9 0 R ] >>
+ /Names 7 0 R
+ /AcroForm <<
+ /Fields [ 5 0 R 6 0 R ]
+ >>
>>
endobj
-2 0 obj
-<<
+2 0 obj <<
/Type /Pages
/Count 1
/Kids [ 3 0 R ]
>>
endobj
-3 0 obj
-<<
+3 0 obj <<
/Type /Page
/Parent 2 0 R
- /Contents 6 0 R
+ /Contents 4 0 R
/MediaBox [ 0 0 300 600 ]
- /Annots [ 8 0 R 9 0 R ]
+ /Annots [ 5 0 R 6 0 R ]
>>
endobj
-6 0 obj
-<<
-/Length 0
+4 0 obj <<
+ /Length 0
>>
stream
endstream
endobj
-8 0 obj
-<<
+5 0 obj <<
/Type /Annot
/Subtype /Link
/Rect [ 100 350 200 380 ]
/Dest (target10)
>>
endobj
-9 0 obj
-<<
+6 0 obj <<
/Type /Annot
/Subtype /Link
/Rect [ 100 400 200 430 ]
/Dest (target100)
>>
endobj
-14 0 obj
-<<
- /Dests 15 0 R
+7 0 obj <<
+ /Dests 8 0 R
>>
endobj
-15 0 obj
-<<
+8 0 obj <<
/Names [
- (target10) 16 0 R
- (target100) 17 0 R
+ (target10) 9 0 R
+ (target100) 10 0 R
]
>>
endobj
-16 0 obj
-<<
+9 0 obj <<
/D [3 0 R /XYZ 100 200 0]
>>
endobj
-17 0 obj
-<<
+10 0 obj <<
/D [3 0 R /XYZ 150 250 0]
>>
endobj
xref
-0 18
+0 11
0000000000 65535 f
0000000015 00000 n
-0000000126 00000 n
-0000000191 00000 n
-0000000000 65535 f
-0000000000 65535 f
-0000000314 00000 n
-0000000000 65535 f
-0000000362 00000 n
-0000000462 00000 n
-0000000000 65535 f
-0000000000 65535 f
-0000000000 65535 f
-0000000000 65535 f
-0000000563 00000 n
-0000000601 00000 n
-0000000683 00000 n
-0000000733 00000 n
-trailer<< /Root 1 0 R /Size 18 >>
+0000000131 00000 n
+0000000196 00000 n
+0000000319 00000 n
+0000000369 00000 n
+0000000469 00000 n
+0000000570 00000 n
+0000000606 00000 n
+0000000686 00000 n
+0000000735 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 11
+>>
startxref
-783
+785
%%EOF
diff --git a/testing/resources/bug_889099.in b/testing/resources/bug_889099.in
new file mode 100644
index 0000000..77ae5f4
--- /dev/null
+++ b/testing/resources/bug_889099.in
@@ -0,0 +1,64 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /AcroForm <<
+ /Fields [4 0 R]
+ /DR 5 0 R
+ >>
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Resources 5 0 R
+ /MediaBox [0 0 300 -400]
+ /Contents 7 0 R
+ /Annots [4 0 R]
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Tx
+ /DA (0 0 0 rg /F1 12 Tf)
+ /F 4
+ /Rect [100 100 200 -130]
+ /T (Text Box)
+ /V (Good :])
+>>
+endobj
+{{object 5 0}} <<
+ /Font <<
+ /F1 6 0 R
+ >>
+>>
+endobj
+{{object 6 0}} <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Helvetica
+>>
+endobj
+{{object 7 0}} <<
+ {{streamlen}}
+>>
+stream
+BT
+0 0 0 rg
+/F1 12 Tf
+100 150 Td
+(Bad :[) Tj
+ET
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/bug_889099.pdf b/testing/resources/bug_889099.pdf
new file mode 100644
index 0000000..a406cd1
--- /dev/null
+++ b/testing/resources/bug_889099.pdf
@@ -0,0 +1,78 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /AcroForm <<
+ /Fields [4 0 R]
+ /DR 5 0 R
+ >>
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Resources 5 0 R
+ /MediaBox [0 0 300 -400]
+ /Contents 7 0 R
+ /Annots [4 0 R]
+>>
+endobj
+4 0 obj <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Tx
+ /DA (0 0 0 rg /F1 12 Tf)
+ /F 4
+ /Rect [100 100 200 -130]
+ /T (Text Box)
+ /V (Good :])
+>>
+endobj
+5 0 obj <<
+ /Font <<
+ /F1 6 0 R
+ >>
+>>
+endobj
+6 0 obj <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Helvetica
+>>
+endobj
+7 0 obj <<
+ /Length 48
+>>
+stream
+BT
+0 0 0 rg
+/F1 12 Tf
+100 150 Td
+(Bad :[) Tj
+ET
+endstream
+endobj
+xref
+0 8
+0000000000 65535 f
+0000000015 00000 n
+0000000122 00000 n
+0000000185 00000 n
+0000000318 00000 n
+0000000475 00000 n
+0000000526 00000 n
+0000000602 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 8
+>>
+startxref
+701
+%%EOF
diff --git a/testing/resources/bug_921.in b/testing/resources/bug_921.in
index a0c2e73..ccd44fb 100644
--- a/testing/resources/bug_921.in
+++ b/testing/resources/bug_921.in
@@ -43,7 +43,7 @@
>>
endobj
{{object 6 0}} <<
-{{streamlen}}
+ {{streamlen}}
>>
stream
BT
diff --git a/testing/resources/bug_921.pdf b/testing/resources/bug_921.pdf
index fb3950e..84e0755 100644
--- a/testing/resources/bug_921.pdf
+++ b/testing/resources/bug_921.pdf
@@ -44,7 +44,7 @@
>>
endobj
6 0 obj <<
-/Length 2784
+ /Length 2784
>>
stream
BT
@@ -68,7 +68,10 @@
0000000287 00000 n
0000000412 00000 n
0000000593 00000 n
-trailer<< /Root 1 0 R /Size 7 >>
+trailer <<
+ /Root 1 0 R
+ /Size 7
+>>
startxref
-3428
+3430
%%EOF
diff --git a/testing/resources/bug_925981.in b/testing/resources/bug_925981.in
index 3d6e37a..f5f8ebe 100644
--- a/testing/resources/bug_925981.in
+++ b/testing/resources/bug_925981.in
@@ -36,7 +36,7 @@
>>
endobj
{{object 6 0}} <<
-{{streamlen}}
+ {{streamlen}}
>>
stream
BT
diff --git a/testing/resources/bug_925981.pdf b/testing/resources/bug_925981.pdf
index 8d856e1..5c35e38 100644
--- a/testing/resources/bug_925981.pdf
+++ b/testing/resources/bug_925981.pdf
@@ -37,7 +37,7 @@
>>
endobj
6 0 obj <<
-/Length 83
+ /Length 83
>>
stream
BT
@@ -64,11 +64,11 @@
0000000309 00000 n
0000000387 00000 n
0000000463 00000 n
-0000000595 00000 n
+0000000597 00000 n
trailer <<
/Root 1 0 R
/Size 8
>>
startxref
-646
+648
%%EOF
diff --git a/testing/resources/bug_972518.pdf b/testing/resources/bug_972518.pdf
index 51185f5..fde54f8 100644
--- a/testing/resources/bug_972518.pdf
+++ b/testing/resources/bug_972518.pdf
@@ -60,7 +60,7 @@
endobj
9 0 obj <<
/Type /Page
- /Parent 2 0 R
+ /Parent 8 0 R
/MediaBox [0 0 612 792]
>>
endobj
diff --git a/testing/resources/dashed_lines.in b/testing/resources/dashed_lines.in
new file mode 100644
index 0000000..b4f74bb
--- /dev/null
+++ b/testing/resources/dashed_lines.in
@@ -0,0 +1,37 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /MediaBox [0 0 200 100]
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+0 0 0 rg
+10 25 m 190 25 l S
+[6 5 4 3 2 1] 5 d
+10 50 m 190 50 l S
+[] 0 d
+10 75 m 190 75 l S
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/dashed_lines.pdf b/testing/resources/dashed_lines.pdf
new file mode 100644
index 0000000..f40a591
--- /dev/null
+++ b/testing/resources/dashed_lines.pdf
@@ -0,0 +1,48 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /MediaBox [0 0 200 100]
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+>>
+endobj
+4 0 obj <<
+ /Length 95
+>>
+stream
+q
+0 0 0 rg
+10 25 m 190 25 l S
+[6 5 4 3 2 1] 5 d
+10 50 m 190 50 l S
+[] 0 d
+10 75 m 190 75 l S
+Q
+endstream
+endobj
+xref
+0 5
+0000000000 65535 f
+0000000015 00000 n
+0000000068 00000 n
+0000000157 00000 n
+0000000226 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 5
+>>
+startxref
+372
+%%EOF
diff --git a/testing/resources/docmdp.in b/testing/resources/docmdp.in
new file mode 100644
index 0000000..a8fe283
--- /dev/null
+++ b/testing/resources/docmdp.in
@@ -0,0 +1,88 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /AcroForm <<
+ /Fields [7 0 R]
+ /SigFlags 3
+ >>
+>>
+endobj
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /MediaBox [0 0 200 300]
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /Annots [7 0 R]
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+0 0 0 rg
+0 290 10 10 re B*
+10 150 50 30 re B*
+0 0 1 rg
+190 290 10 10 re B*
+70 232 50 30 re B*
+0 1 0 rg
+190 0 10 10 re B*
+130 150 50 30 re B*
+1 0 0 rg
+0 0 10 10 re B*
+70 67 50 30 re B*
+Q
+endstream
+endobj
+{{object 5 0}} <<
+ /Type /Sig
+ /Reference [
+ <<
+ /Type /SigRef
+ /TransformMethod /DocMDP
+ /TransformParams <<
+ /Type /TransformParams
+ /P 1
+ /V /1.2
+ >>
+ >>
+ ]
+>>
+endobj
+{{object 6 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /BBox [0 0 0 0]
+ /Length 0
+>>
+stream
+endstream
+endobj
+{{object 7 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Sig
+ /F 132
+ /Rect [0 0 0 0]
+ /P 3 0 R
+ /T (Signature1)
+ /V 5 0 R
+ /DV 5 0 R
+ /AP <<
+ /N 6 0 R
+ >>
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/docmdp.pdf b/testing/resources/docmdp.pdf
new file mode 100644
index 0000000..0191a00
--- /dev/null
+++ b/testing/resources/docmdp.pdf
@@ -0,0 +1,102 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /AcroForm <<
+ /Fields [7 0 R]
+ /SigFlags 3
+ >>
+>>
+endobj
+endobj
+2 0 obj <<
+ /Type /Pages
+ /MediaBox [0 0 200 300]
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /Annots [7 0 R]
+>>
+endobj
+4 0 obj <<
+ /Length 188
+>>
+stream
+q
+0 0 0 rg
+0 290 10 10 re B*
+10 150 50 30 re B*
+0 0 1 rg
+190 290 10 10 re B*
+70 232 50 30 re B*
+0 1 0 rg
+190 0 10 10 re B*
+130 150 50 30 re B*
+1 0 0 rg
+0 0 10 10 re B*
+70 67 50 30 re B*
+Q
+endstream
+endobj
+5 0 obj <<
+ /Type /Sig
+ /Reference [
+ <<
+ /Type /SigRef
+ /TransformMethod /DocMDP
+ /TransformParams <<
+ /Type /TransformParams
+ /P 1
+ /V /1.2
+ >>
+ >>
+ ]
+>>
+endobj
+6 0 obj <<
+ /Type /XObject
+ /Subtype /Form
+ /BBox [0 0 0 0]
+ /Length 0
+>>
+stream
+endstream
+endobj
+7 0 obj <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Sig
+ /F 132
+ /Rect [0 0 0 0]
+ /P 3 0 R
+ /T (Signature1)
+ /V 5 0 R
+ /DV 5 0 R
+ /AP <<
+ /N 6 0 R
+ >>
+>>
+endobj
+xref
+0 8
+0000000000 65535 f
+0000000015 00000 n
+0000000131 00000 n
+0000000220 00000 n
+0000000307 00000 n
+0000000547 00000 n
+0000000760 00000 n
+0000000862 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 8
+>>
+startxref
+1034
+%%EOF
diff --git a/testing/resources/embedded_attachments_invalid_data.in b/testing/resources/embedded_attachments_invalid_data.in
new file mode 100644
index 0000000..26545a4
--- /dev/null
+++ b/testing/resources/embedded_attachments_invalid_data.in
@@ -0,0 +1,41 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Names 4 0 R
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 612 792]
+ /Resources <<
+ /ProcSet [/PDF]
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ /EmbeddedFiles 5 0 R
+>>
+endobj
+{{object 5 0}} <<
+ /Names [(1.txt) 6 0 R]
+>>
+endobj
+{{object 6 0}} <<
+ /Type /Filespec
+ /Desc ()
+ /F (1.txt)
+ /UF (1.txt)
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/embedded_attachments_invalid_data.pdf b/testing/resources/embedded_attachments_invalid_data.pdf
new file mode 100644
index 0000000..8a830e2
--- /dev/null
+++ b/testing/resources/embedded_attachments_invalid_data.pdf
@@ -0,0 +1,54 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Names 4 0 R
+ /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 612 792]
+ /Resources <<
+ /ProcSet [/PDF]
+ >>
+>>
+endobj
+4 0 obj <<
+ /EmbeddedFiles 5 0 R
+>>
+endobj
+5 0 obj <<
+ /Names [(1.txt) 6 0 R]
+>>
+endobj
+6 0 obj <<
+ /Type /Filespec
+ /Desc ()
+ /F (1.txt)
+ /UF (1.txt)
+>>
+endobj
+xref
+0 7
+0000000000 65535 f
+0000000015 00000 n
+0000000083 00000 n
+0000000146 00000 n
+0000000264 00000 n
+0000000308 00000 n
+0000000354 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 7
+>>
+startxref
+431
+%%EOF
diff --git a/testing/resources/find_text_consecutive.in b/testing/resources/find_text_consecutive.in
index 9e35e1d..d484584 100644
--- a/testing/resources/find_text_consecutive.in
+++ b/testing/resources/find_text_consecutive.in
@@ -29,7 +29,7 @@
>>
endobj
{{object 5 0}} <<
-{{streamlen}}
+ {{streamlen}}
>>
stream
BT
diff --git a/testing/resources/find_text_consecutive.pdf b/testing/resources/find_text_consecutive.pdf
index 5083d75..bd78380 100644
--- a/testing/resources/find_text_consecutive.pdf
+++ b/testing/resources/find_text_consecutive.pdf
@@ -30,7 +30,7 @@
>>
endobj
5 0 obj <<
-/Length 51
+ /Length 51
>>
stream
BT
@@ -53,5 +53,5 @@
/Size 6
>>
startxref
-465
+467
%%EOF
diff --git a/testing/resources/fonts/SymbolNeu.ttf b/testing/resources/fonts/SymbolNeu.ttf
new file mode 100644
index 0000000..08932c9
--- /dev/null
+++ b/testing/resources/fonts/SymbolNeu.ttf
Binary files differ
diff --git a/testing/resources/get_page_aaction.in b/testing/resources/get_page_aaction.in
new file mode 100644
index 0000000..83f0c01
--- /dev/null
+++ b/testing/resources/get_page_aaction.in
@@ -0,0 +1,50 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Kids [
+ 3 0 R
+ 4 0 R
+ ]
+ /Count 2
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 612 792]
+ /AA <<
+ /O <<
+ /F (\\\\127.0.0.1\\c$\\Program Files\\test.exe)
+ /D [1 /Fit]
+ /S /GoToE
+ >>
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 612 792]
+ /AA <<
+ /O <<
+ /F (\\\\127.0.0.1\\c$\\Program Files\\test_page_two_open.exe)
+ /D [1 /Fit]
+ /S /GoToE
+ >>
+ /C <<
+ /F (\\\\127.0.0.1\\c$\\Program Files\\test_page_two_close.exe)
+ /D [1 /Fit]
+ /S /GoToE
+ >>
+ >>
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/get_page_aaction.pdf b/testing/resources/get_page_aaction.pdf
new file mode 100644
index 0000000..7d46651
--- /dev/null
+++ b/testing/resources/get_page_aaction.pdf
@@ -0,0 +1,61 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /Kids [
+ 3 0 R
+ 4 0 R
+ ]
+ /Count 2
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 612 792]
+ /AA <<
+ /O <<
+ /F (\\\\127.0.0.1\\c$\\Program Files\\test.exe)
+ /D [1 /Fit]
+ /S /GoToE
+ >>
+ >>
+>>
+endobj
+4 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 612 792]
+ /AA <<
+ /O <<
+ /F (\\\\127.0.0.1\\c$\\Program Files\\test_page_two_open.exe)
+ /D [1 /Fit]
+ /S /GoToE
+ >>
+ /C <<
+ /F (\\\\127.0.0.1\\c$\\Program Files\\test_page_two_close.exe)
+ /D [1 /Fit]
+ /S /GoToE
+ >>
+ >>
+>>
+endobj
+xref
+0 5
+0000000000 65535 f
+0000000015 00000 n
+0000000068 00000 n
+0000000149 00000 n
+0000000345 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 5
+>>
+startxref
+675
+%%EOF
diff --git a/testing/resources/gotoe_action.in b/testing/resources/gotoe_action.in
new file mode 100644
index 0000000..04b7cf0
--- /dev/null
+++ b/testing/resources/gotoe_action.in
@@ -0,0 +1,48 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /MediaBox [0 0 200 200]
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Annots [4 0 R]
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Annot
+ /Subtype /Link
+ /FT /Tx
+ /Ff 29360128
+ /A 5 0 R
+ /T (txtName)
+ /F 4
+ /M (D:20150514070426+05'30')
+ /Rect [1 1 199 199]
+ /BS <<
+ /W 1
+ /S /S
+ >>
+ /DA (/Helv 0 Tf 0 0 0 rg)
+ /V ()
+>>
+endobj
+{{object 5 0}} <<
+ /S /GoToE
+ /D [1 /Fit]
+ /F (ExampleFile.pdf)
+>>
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/gotoe_action.pdf b/testing/resources/gotoe_action.pdf
new file mode 100644
index 0000000..8d588e0
--- /dev/null
+++ b/testing/resources/gotoe_action.pdf
@@ -0,0 +1,60 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /MediaBox [0 0 200 200]
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Annots [4 0 R]
+>>
+endobj
+4 0 obj <<
+ /Type /Annot
+ /Subtype /Link
+ /FT /Tx
+ /Ff 29360128
+ /A 5 0 R
+ /T (txtName)
+ /F 4
+ /M (D:20150514070426+05'30')
+ /Rect [1 1 199 199]
+ /BS <<
+ /W 1
+ /S /S
+ >>
+ /DA (/Helv 0 Tf 0 0 0 rg)
+ /V ()
+>>
+endobj
+5 0 obj <<
+ /S /GoToE
+ /D [1 /Fit]
+ /F (ExampleFile.pdf)
+>>
+>>
+endobj
+xref
+0 6
+0000000000 65535 f
+0000000015 00000 n
+0000000068 00000 n
+0000000157 00000 n
+0000000226 00000 n
+0000000460 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 6
+>>
+startxref
+533
+%%EOF
diff --git a/testing/resources/gray-alpha.jp2 b/testing/resources/gray-alpha.jp2
new file mode 100644
index 0000000..f5e3f34
--- /dev/null
+++ b/testing/resources/gray-alpha.jp2
Binary files differ
diff --git a/testing/resources/gray.jp2 b/testing/resources/gray.jp2
new file mode 100644
index 0000000..53293bd
--- /dev/null
+++ b/testing/resources/gray.jp2
Binary files differ
diff --git a/testing/resources/hebrew_mirrored.in b/testing/resources/hebrew_mirrored.in
new file mode 100644
index 0000000..09ede5d
--- /dev/null
+++ b/testing/resources/hebrew_mirrored.in
@@ -0,0 +1,160 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /MediaBox [0 0 200 200]
+ /Resources <<
+ /ProcSet [/PDF /Text]
+ /Font <<
+ /F1 5 0 R
+ >>
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+BT
+/F1 12 Tf
+150 160 Td
+[<01>2<02>2<03>-4<02>2<04>5<05>]TJ
+ET
+
+BT
+0 100 Td
+-1 0 0 1 50 40 Tm
+[<01>5<05>]TJ
+ET
+endstream
+endobj
+{{object 5 0}} <<
+ /Type /Font
+ /Subtype /TrueType
+ /BaseFont /BAAAAA+NotoSansHebrew-Regular
+ /FirstChar 0
+ /FontDescriptor 6 0 R
+ /LastChar 5
+ /ToUnicode 8 0 R
+ /Widths [600 294 235 645 397 542]
+>>
+endobj
+{{object 6 0}} <<
+ /Type /FontDescriptor
+ /Ascent 1069
+ /CapHeight 869
+ /Descent -293
+ /Flags 4
+ /FontBBox [-210 -252 716 869]
+ /FontFile2 7 0 R
+ /FontName /BAAAAA+NotoSansHebrew-Regular
+ /ItalicAngle 0
+ /StemV 80
+>>
+endobj
+{{object 7 0}} <<
+ /Filter [/ASCII85Decode /FlateDecode]
+ {{streamlen}}
+>>
+stream
+GhVOeCMXsCFZt#(%p<i9AU.uO6]Ye/ifKHXbLV!I1<gsgHY$Eg-tbPt@rk=Ap?l+FQ/u;^8JASXVQS>V
+nuG<j"nF$,Rii%*89oFrq6<34,1m)cJ0IF5(22*&"c<?^q>^jZEkc[.-qdl^dk1F&,fKT9F2)eaH/1QX
+<gN4)W0n]g:798;]<j42dYR+b7&$II`er0Do2PVi55OH.DU&3oF7^D>=p%W;pT6OOkC.gL0;aQZ%EQ]$
+CMpO/ouiOe*=oqMFW5p^ge<JWIt[XVg[cqr8aR%^g[TKTpYK&S\F4)WJbd&hhE(,./q'ef/\k-FOKm`h
+f=q)o6_0Sd-Ap"u/`'bn6!o+8Zu;5]Q^2D[J,6jbf.d4Ck't7.im"Z=^^/7D4VXdnJ*hX.ia&@S<Zl_D
+Xq`#Xlf3.[Q6GL-.IS+nj<JEQ1+=HefoeH8Qi0[)c<.\^P$4LTD?DamV./;?-PDR^8[b$k8'^LB2C4&'
+NUg=HC6k8A'.Knb7Wh)elDCX74)L`tc?`Z\Sb;i$>AElUWZdj+!8FWu:gnKPdkmGSM;%q39@mC[7<ZEH
+BP,jX746Y[8VL'e=mlrkg+[&/4Xn#1'(72pCc`cXNN+/Z0?DM&g%BgA@co7e)9J3X;X\\(5@"=N^/d""
+T`@poCcT@G`,Q43B>^KnLk;$<#C1`ZRI:qa#-tMr)R?VE.RR\7=sKe@N)#YuA]JfMaHgYN/!?c&OsZTe
+^cXe0%l-1*krrj[$8W`Wl)*K?>t9dJirfrR7MYH\=XPN5Q2qL_I4eudg4b]KRT\<OAl/T-PZ'?3dZ$p;
+<A/YSC[`O')u&1O:[moi)'2=Vd5KR72=0E6LAR(+/#@80mS5hidn)NRbS3]PMfW2+=&LegaZXffSM\+*
+%@ESe'CB7@?)cYMPG3X<A98q?pK.WHgPW4%FOOV*1/i@`;U-_0)m/crX^pV$",X/"BsMn?;R::1,iO?7
+d*"n-c#gLp.bPO!Jk.*r-gP*5[&0>a[qOG`>on_9`Lhgl?)c@P!H#2#PFffIEAQP^TS_DS:Xem?lK2!M
+h#c+\A=NN1)ZW-WA38,70l'k`MSW-$8rA0><3W]ffk"ZqCrdIAQBE9>j@mZ'<HHGofhsV-;mOl@]Mb1L
+qSIIYnkLM"Z97EVYElh]%[>>bb#jgf**OlY[IHaK5,@C./bHbDalE":hWu.1N,@;S&gl,n3:<uV$;N?L
+*(2O_s!JrF=fbn$ki"!LOY9qefX_V\<NFjuO0ngY8TpJ(,;39KIP'3^N)o:sU^S>oeCuK%7\,dgAiUN1
+E!0r_=$ol73>k&7]_*E"mUSrX[62980+uUk50ME#I*V:L4N8s+bi=/qMstu,ntJ5f[(HSa5@JhaGYD:Q
+]auqs7`nFM/]DAtfVG=ND6[_hnTB=9[;GHTf&F!MBNQP7P.LnAZ2VP"Ploh`?NTa8nEUYFj3^8B4<?t>
+4SBp^*fhF-*fhL/*r?NkO.Sgf4:WJt*n-E0W]KmAKl)1[L<V@'WjXmHBl;cn9--aK@lTjrE@*#k?-6on
+,:;=;+3B=[oI&q3p0&\dDEW8m^^H(gHjR8,7ldq.4hG]/>/mf;Hl1F^:B&.Xiqu!h(RA"GpXOmI2eJ(p
+-Ufu7H4'I5!71?\VoB`KajX::mq>=Rr&Q;g]E7s$-kZ.E)/3T4mfoT]_V3^s3J#ALKI7nF?TacHK]qhj
+L<ncjl]HN[$fPFZ;`(\GZEhSIf%/9H@>qA*jpO7b7bY=!,<-,5gQ6!\>V]AQ)^-GB`&C"Q_Tt5F^2b04
+H^dXF*&#KkrUdWPo<maGSjIV2C<A6%Y#l1eDB-11=5MCe/mQ;H^`5k(:$aRZO01rVm/hX\fYmD8f*Dj@
+M@i=qKg0)_(>4O.G!a5^E2>bO$]QiDc\2C6pdgJSEti>7EGJ-(WqC@QJiDd@NbtVSKCL`LBr7+Q7e(#%
+mfT9"IKcR0OZpuAD#Y..(\(Ya*`U>DQ9W"+'>%SR14Rbu3okUD1UoZtSAfKYf.IftGdnZe:>-X$>r]=V
+G-"HZ$ThCqcd_nA%GR%YOrq)?cB9h+fAIVB(kUJ,MLq_e+>iX=AF@bgIcap`;p5$2F+]o@5&$NLhu:.I
+2<(<.=8A484n4$P`>fQKA"tBRWbq]tDur6ODulS&f!BB<ZPSc21WW3#buP0(+9gU#Z,bH/SFFh24G?`5
+dOt)^QkNbrnhfi)90SEq&n`q[F;s7b!nXc$g#@aPQN3qbFeWl+!e=Zt`EF'M1(Bm%7HR8DbV4gg<TZ"1
+'QY,(Ci/)_["1h766XD'&ADrraHq=DH%OF$jsfp?jsbDCGm`Bc4nAYbhNNN6HSW+QHSVNRk<)"p3=KZH
+@5,_A#.Kh(3M>-L01N,n^DOYar*o;enr(m9jVO\;jVOZqjVO[\jEPdIJ4jW(1Ou\+n/U@A-XG!&R,,`f
+2W1\3O?*%L7g,>@T`!f8eMl#PBrq/m2=C@'l+[6\!TC@KAA[MWaFrFRGAPIZkLE8R^YhJV5O]iFIg_\G
+iO=6VK\u;C'>=b?fe%PhWL9K1abNRi>otj#+Z';<n+i%OCHc:GQ`]p-obCOtip.a^cf<UjY!FG)PRTLL
++"jYG+4_li`+jB7`VE<s"4AVj".>pGko/fq8/"=:R5@Xn#Q]c2U1bel!_n`0,C>VAYsbZH&PH@FJ$f#/
+0td=Kg'>AR25F7&YOo!<mI]$*rRjT2aR!Q]%Fi)aOtJSn?+UM2'/_)[:p`0l2g4bJBatZEkin+Um_FDG
+q;M6)[I9OhMIS=<lFaD[8\^p:_H)J0l0'R9&ZuGO_>dFFCb.uJQlb(Q4<5Au742_H^C[$nr;0XJ4b3Tg
+[;48b%t<.Gdkk?8S<Vi`;Poe&S_S0PK8iOs+@Vd0N*gN+=Plp50G^#i&Kg"CSmrqAknTi"D:*>YSUrma
+g=r&8<Pj&ND`$13'HCOjG_kBC'T)*ekL$!:6XuRpIX?)+^V5+S2cXNJB2ec80tlUL?G#q-m,hbS%$"V@
+p"JQ?igh1:m'G\$*s_?+,4a,JI@3GnX*+U"_Zr.(+*>1jkFAnU9>ZXAU\g3ArglE9&,uBU"0[/n3+!B'
+W[)MC"?*YACpqa=bi'p-F[R+Y&Jh':>DS9I;-PEL7\\5FC1<[_U$dQmm+CP5IQgiD>SQGm;oRG4k5.U/
+0D?:mI/C=6)f;AM:/Ut^\F':\li2"qaZh<t\"cP;!(<3T?G(>oo5D,:=$o+C?]KqCU?geEFjWG&V"PRp
+&9$#`m3'G9"&tosqAZg/@%\C2I=Z6p1:0Yl"+@)T)so1U@.,fWm/arHR8d:MJXMQVC8K5]=Jm_KIXjdB
+83[K$Qla[fJ0ST]0*O=-3O'X`"r\'Q&`>h%Y8I@YUo\`'^eHY_XTc5&7^\ik\4-8s4VZgTU@:6TdipBk
+I9NLLkSE!O'mP*-*Z_.eTF%\P6@O])QsDsH.[1,u#K\!Fdp$""eZ=M1#$Z$(ZO5i+BWp@?TH^qnB%D7/
+h#sWFD^p`NF:Yfb85;E[:q^5Oc;Ss`!7br9/Ss$74_<U(m:SQ#s-3*J/tuGNgXsJtKtqF1bXL_J,7?@i
+'BEj1Ilon*aW+o61dn83AfE7FcXE_u9)ggC=\p"(7u!%6Y7^]'TMPP?@sg_akMgC`EYfUsdCeJO#2(2D
+hQ;Br<4m&15-"(TYi[P!iN8/Ok#@VVm[UO"bZs@LQZDI&pjXBX+4dX`csI+Oe#J=,^c=d&n:"<LdpP?/
+57NYh_**Nu%GYGtPrZ:2pA@k$5I#/DZ?u(p.mKdY_mpagr"[aHSA,e@EE+%EftN*$X1t;gS`@?f>ZH3j
+dG.dX,4]pij-88?@>Es&4"OKq4Pb_H)9:8fq`(0Ie[V>KnkIYS^43jk=+).,aXsOX(4cqM6KaT:7HMS5
+5VqW;.CtD!kNC3=e+gS7q$"nkh'kl'g`'1Rk*3jhL&JE!FZWhT#NI(1R)?@JnB0)&ZC7m>1Wud3mOB<F
+<?1UlEeaPIK4D$_#X<cE%HM2B7nrD$SI/J?MJ9$\[%L0[X#c.:>Z)WgX'F4+E];'H25pPfMT*QlPBH<B
+2jEaIeeu%@g_SZi^/+`r>koI@22thoJ$T#-Ig:8=C[r)9A@gh.nJ+P$rtk!a7oB995m%NRR_tlI7kn;C
+L?s85#0T`VHa<+&O5K'$Eo(6pn-XUq4Y6-9Re-B$rhtBq!Tfi]GEbPb2*A\G,P/,p;d\J77]S.F#9J`B
+DopA\Sjp8ME&Btm*&[^S4k/0Z~>
+endstream
+endobj
+{{object 8 0}} <<
+ {{streamlen}}
+>>
+stream
+/CIDInit/ProcSet findresource begin
+12 dict begin
+begincmap
+/CIDSystemInfo<<
+/Registry (Adobe)
+/Ordering (UCS)
+/Supplement 0
+>> def
+/CMapName/Adobe-Identity-UCS def
+/CMapType 2 def
+1 begincodespacerange
+<00> <FF>
+endcodespacerange
+5 beginbfchar
+<01> <05DF>
+<02> <05D9>
+<03> <05DE>
+<04> <05E0>
+<05> <05D1>
+endbfchar
+endcmap
+CMapName currentdict /CMap defineresource pop
+end
+end
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/hebrew_mirrored.pdf b/testing/resources/hebrew_mirrored.pdf
new file mode 100644
index 0000000..1134350
--- /dev/null
+++ b/testing/resources/hebrew_mirrored.pdf
@@ -0,0 +1,175 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /MediaBox [0 0 200 200]
+ /Resources <<
+ /ProcSet [/PDF /Text]
+ /Font <<
+ /F1 5 0 R
+ >>
+ >>
+>>
+endobj
+4 0 obj <<
+ /Length 110
+>>
+stream
+BT
+/F1 12 Tf
+150 160 Td
+[<01>2<02>2<03>-4<02>2<04>5<05>]TJ
+ET
+
+BT
+0 100 Td
+-1 0 0 1 50 40 Tm
+[<01>5<05>]TJ
+ET
+endstream
+endobj
+5 0 obj <<
+ /Type /Font
+ /Subtype /TrueType
+ /BaseFont /BAAAAA+NotoSansHebrew-Regular
+ /FirstChar 0
+ /FontDescriptor 6 0 R
+ /LastChar 5
+ /ToUnicode 8 0 R
+ /Widths [600 294 235 645 397 542]
+>>
+endobj
+6 0 obj <<
+ /Type /FontDescriptor
+ /Ascent 1069
+ /CapHeight 869
+ /Descent -293
+ /Flags 4
+ /FontBBox [-210 -252 716 869]
+ /FontFile2 7 0 R
+ /FontName /BAAAAA+NotoSansHebrew-Regular
+ /ItalicAngle 0
+ /StemV 80
+>>
+endobj
+7 0 obj <<
+ /Filter [/ASCII85Decode /FlateDecode]
+ /Length 4240
+>>
+stream
+GhVOeCMXsCFZt#(%p<i9AU.uO6]Ye/ifKHXbLV!I1<gsgHY$Eg-tbPt@rk=Ap?l+FQ/u;^8JASXVQS>V
+nuG<j"nF$,Rii%*89oFrq6<34,1m)cJ0IF5(22*&"c<?^q>^jZEkc[.-qdl^dk1F&,fKT9F2)eaH/1QX
+<gN4)W0n]g:798;]<j42dYR+b7&$II`er0Do2PVi55OH.DU&3oF7^D>=p%W;pT6OOkC.gL0;aQZ%EQ]$
+CMpO/ouiOe*=oqMFW5p^ge<JWIt[XVg[cqr8aR%^g[TKTpYK&S\F4)WJbd&hhE(,./q'ef/\k-FOKm`h
+f=q)o6_0Sd-Ap"u/`'bn6!o+8Zu;5]Q^2D[J,6jbf.d4Ck't7.im"Z=^^/7D4VXdnJ*hX.ia&@S<Zl_D
+Xq`#Xlf3.[Q6GL-.IS+nj<JEQ1+=HefoeH8Qi0[)c<.\^P$4LTD?DamV./;?-PDR^8[b$k8'^LB2C4&'
+NUg=HC6k8A'.Knb7Wh)elDCX74)L`tc?`Z\Sb;i$>AElUWZdj+!8FWu:gnKPdkmGSM;%q39@mC[7<ZEH
+BP,jX746Y[8VL'e=mlrkg+[&/4Xn#1'(72pCc`cXNN+/Z0?DM&g%BgA@co7e)9J3X;X\\(5@"=N^/d""
+T`@poCcT@G`,Q43B>^KnLk;$<#C1`ZRI:qa#-tMr)R?VE.RR\7=sKe@N)#YuA]JfMaHgYN/!?c&OsZTe
+^cXe0%l-1*krrj[$8W`Wl)*K?>t9dJirfrR7MYH\=XPN5Q2qL_I4eudg4b]KRT\<OAl/T-PZ'?3dZ$p;
+<A/YSC[`O')u&1O:[moi)'2=Vd5KR72=0E6LAR(+/#@80mS5hidn)NRbS3]PMfW2+=&LegaZXffSM\+*
+%@ESe'CB7@?)cYMPG3X<A98q?pK.WHgPW4%FOOV*1/i@`;U-_0)m/crX^pV$",X/"BsMn?;R::1,iO?7
+d*"n-c#gLp.bPO!Jk.*r-gP*5[&0>a[qOG`>on_9`Lhgl?)c@P!H#2#PFffIEAQP^TS_DS:Xem?lK2!M
+h#c+\A=NN1)ZW-WA38,70l'k`MSW-$8rA0><3W]ffk"ZqCrdIAQBE9>j@mZ'<HHGofhsV-;mOl@]Mb1L
+qSIIYnkLM"Z97EVYElh]%[>>bb#jgf**OlY[IHaK5,@C./bHbDalE":hWu.1N,@;S&gl,n3:<uV$;N?L
+*(2O_s!JrF=fbn$ki"!LOY9qefX_V\<NFjuO0ngY8TpJ(,;39KIP'3^N)o:sU^S>oeCuK%7\,dgAiUN1
+E!0r_=$ol73>k&7]_*E"mUSrX[62980+uUk50ME#I*V:L4N8s+bi=/qMstu,ntJ5f[(HSa5@JhaGYD:Q
+]auqs7`nFM/]DAtfVG=ND6[_hnTB=9[;GHTf&F!MBNQP7P.LnAZ2VP"Ploh`?NTa8nEUYFj3^8B4<?t>
+4SBp^*fhF-*fhL/*r?NkO.Sgf4:WJt*n-E0W]KmAKl)1[L<V@'WjXmHBl;cn9--aK@lTjrE@*#k?-6on
+,:;=;+3B=[oI&q3p0&\dDEW8m^^H(gHjR8,7ldq.4hG]/>/mf;Hl1F^:B&.Xiqu!h(RA"GpXOmI2eJ(p
+-Ufu7H4'I5!71?\VoB`KajX::mq>=Rr&Q;g]E7s$-kZ.E)/3T4mfoT]_V3^s3J#ALKI7nF?TacHK]qhj
+L<ncjl]HN[$fPFZ;`(\GZEhSIf%/9H@>qA*jpO7b7bY=!,<-,5gQ6!\>V]AQ)^-GB`&C"Q_Tt5F^2b04
+H^dXF*&#KkrUdWPo<maGSjIV2C<A6%Y#l1eDB-11=5MCe/mQ;H^`5k(:$aRZO01rVm/hX\fYmD8f*Dj@
+M@i=qKg0)_(>4O.G!a5^E2>bO$]QiDc\2C6pdgJSEti>7EGJ-(WqC@QJiDd@NbtVSKCL`LBr7+Q7e(#%
+mfT9"IKcR0OZpuAD#Y..(\(Ya*`U>DQ9W"+'>%SR14Rbu3okUD1UoZtSAfKYf.IftGdnZe:>-X$>r]=V
+G-"HZ$ThCqcd_nA%GR%YOrq)?cB9h+fAIVB(kUJ,MLq_e+>iX=AF@bgIcap`;p5$2F+]o@5&$NLhu:.I
+2<(<.=8A484n4$P`>fQKA"tBRWbq]tDur6ODulS&f!BB<ZPSc21WW3#buP0(+9gU#Z,bH/SFFh24G?`5
+dOt)^QkNbrnhfi)90SEq&n`q[F;s7b!nXc$g#@aPQN3qbFeWl+!e=Zt`EF'M1(Bm%7HR8DbV4gg<TZ"1
+'QY,(Ci/)_["1h766XD'&ADrraHq=DH%OF$jsfp?jsbDCGm`Bc4nAYbhNNN6HSW+QHSVNRk<)"p3=KZH
+@5,_A#.Kh(3M>-L01N,n^DOYar*o;enr(m9jVO\;jVOZqjVO[\jEPdIJ4jW(1Ou\+n/U@A-XG!&R,,`f
+2W1\3O?*%L7g,>@T`!f8eMl#PBrq/m2=C@'l+[6\!TC@KAA[MWaFrFRGAPIZkLE8R^YhJV5O]iFIg_\G
+iO=6VK\u;C'>=b?fe%PhWL9K1abNRi>otj#+Z';<n+i%OCHc:GQ`]p-obCOtip.a^cf<UjY!FG)PRTLL
++"jYG+4_li`+jB7`VE<s"4AVj".>pGko/fq8/"=:R5@Xn#Q]c2U1bel!_n`0,C>VAYsbZH&PH@FJ$f#/
+0td=Kg'>AR25F7&YOo!<mI]$*rRjT2aR!Q]%Fi)aOtJSn?+UM2'/_)[:p`0l2g4bJBatZEkin+Um_FDG
+q;M6)[I9OhMIS=<lFaD[8\^p:_H)J0l0'R9&ZuGO_>dFFCb.uJQlb(Q4<5Au742_H^C[$nr;0XJ4b3Tg
+[;48b%t<.Gdkk?8S<Vi`;Poe&S_S0PK8iOs+@Vd0N*gN+=Plp50G^#i&Kg"CSmrqAknTi"D:*>YSUrma
+g=r&8<Pj&ND`$13'HCOjG_kBC'T)*ekL$!:6XuRpIX?)+^V5+S2cXNJB2ec80tlUL?G#q-m,hbS%$"V@
+p"JQ?igh1:m'G\$*s_?+,4a,JI@3GnX*+U"_Zr.(+*>1jkFAnU9>ZXAU\g3ArglE9&,uBU"0[/n3+!B'
+W[)MC"?*YACpqa=bi'p-F[R+Y&Jh':>DS9I;-PEL7\\5FC1<[_U$dQmm+CP5IQgiD>SQGm;oRG4k5.U/
+0D?:mI/C=6)f;AM:/Ut^\F':\li2"qaZh<t\"cP;!(<3T?G(>oo5D,:=$o+C?]KqCU?geEFjWG&V"PRp
+&9$#`m3'G9"&tosqAZg/@%\C2I=Z6p1:0Yl"+@)T)so1U@.,fWm/arHR8d:MJXMQVC8K5]=Jm_KIXjdB
+83[K$Qla[fJ0ST]0*O=-3O'X`"r\'Q&`>h%Y8I@YUo\`'^eHY_XTc5&7^\ik\4-8s4VZgTU@:6TdipBk
+I9NLLkSE!O'mP*-*Z_.eTF%\P6@O])QsDsH.[1,u#K\!Fdp$""eZ=M1#$Z$(ZO5i+BWp@?TH^qnB%D7/
+h#sWFD^p`NF:Yfb85;E[:q^5Oc;Ss`!7br9/Ss$74_<U(m:SQ#s-3*J/tuGNgXsJtKtqF1bXL_J,7?@i
+'BEj1Ilon*aW+o61dn83AfE7FcXE_u9)ggC=\p"(7u!%6Y7^]'TMPP?@sg_akMgC`EYfUsdCeJO#2(2D
+hQ;Br<4m&15-"(TYi[P!iN8/Ok#@VVm[UO"bZs@LQZDI&pjXBX+4dX`csI+Oe#J=,^c=d&n:"<LdpP?/
+57NYh_**Nu%GYGtPrZ:2pA@k$5I#/DZ?u(p.mKdY_mpagr"[aHSA,e@EE+%EftN*$X1t;gS`@?f>ZH3j
+dG.dX,4]pij-88?@>Es&4"OKq4Pb_H)9:8fq`(0Ie[V>KnkIYS^43jk=+).,aXsOX(4cqM6KaT:7HMS5
+5VqW;.CtD!kNC3=e+gS7q$"nkh'kl'g`'1Rk*3jhL&JE!FZWhT#NI(1R)?@JnB0)&ZC7m>1Wud3mOB<F
+<?1UlEeaPIK4D$_#X<cE%HM2B7nrD$SI/J?MJ9$\[%L0[X#c.:>Z)WgX'F4+E];'H25pPfMT*QlPBH<B
+2jEaIeeu%@g_SZi^/+`r>koI@22thoJ$T#-Ig:8=C[r)9A@gh.nJ+P$rtk!a7oB995m%NRR_tlI7kn;C
+L?s85#0T`VHa<+&O5K'$Eo(6pn-XUq4Y6-9Re-B$rhtBq!Tfi]GEbPb2*A\G,P/,p;d\J77]S.F#9J`B
+DopA\Sjp8ME&Btm*&[^S4k/0Z~>
+endstream
+endobj
+8 0 obj <<
+ /Length 377
+>>
+stream
+/CIDInit/ProcSet findresource begin
+12 dict begin
+begincmap
+/CIDSystemInfo<<
+/Registry (Adobe)
+/Ordering (UCS)
+/Supplement 0
+>> def
+/CMapName/Adobe-Identity-UCS def
+/CMapType 2 def
+1 begincodespacerange
+<00> <FF>
+endcodespacerange
+5 beginbfchar
+<01> <05DF>
+<02> <05D9>
+<03> <05DE>
+<04> <05E0>
+<05> <05D1>
+endbfchar
+endcmap
+CMapName currentdict /CMap defineresource pop
+end
+end
+endstream
+endobj
+xref
+0 9
+0000000000 65535 f
+0000000015 00000 n
+0000000068 00000 n
+0000000131 00000 n
+0000000309 00000 n
+0000000471 00000 n
+0000000678 00000 n
+0000000905 00000 n
+0000005238 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 9
+>>
+startxref
+5667
+%%EOF
diff --git a/testing/resources/hello_world.in b/testing/resources/hello_world.in
index d3cf265..754820a 100644
--- a/testing/resources/hello_world.in
+++ b/testing/resources/hello_world.in
@@ -6,9 +6,9 @@
endobj
{{object 2 0}} <<
/Type /Pages
- /MediaBox [ 0 0 200 200 ]
+ /MediaBox [0 0 200 200]
/Count 1
- /Kids [ 3 0 R ]
+ /Kids [3 0 R]
>>
endobj
{{object 3 0}} <<
@@ -36,6 +36,7 @@
>>
endobj
{{object 6 0}} <<
+ % Note this object deliberately does not use {{streamlen}}.
>>
stream
BT
diff --git a/testing/resources/hello_world.pdf b/testing/resources/hello_world.pdf
index dcde8f7..48c714b 100644
--- a/testing/resources/hello_world.pdf
+++ b/testing/resources/hello_world.pdf
@@ -7,9 +7,9 @@
endobj
2 0 obj <<
/Type /Pages
- /MediaBox [ 0 0 200 200 ]
+ /MediaBox [0 0 200 200]
/Count 1
- /Kids [ 3 0 R ]
+ /Kids [3 0 R]
>>
endobj
3 0 obj <<
@@ -37,6 +37,7 @@
>>
endobj
6 0 obj <<
+ % Note this object deliberately does not use /Length 83.
>>
stream
BT
@@ -54,11 +55,14 @@
0000000000 65535 f
0000000015 00000 n
0000000068 00000 n
-0000000161 00000 n
-0000000303 00000 n
-0000000381 00000 n
-0000000457 00000 n
-trailer<< /Root 1 0 R /Size 7 >>
+0000000157 00000 n
+0000000299 00000 n
+0000000377 00000 n
+0000000453 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 7
+>>
startxref
-578
+633
%%EOF
diff --git a/testing/resources/hello_world_2_pages.in b/testing/resources/hello_world_2_pages.in
new file mode 100644
index 0000000..ec33354
--- /dev/null
+++ b/testing/resources/hello_world_2_pages.in
@@ -0,0 +1,67 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 2
+ /Kids [3 0 R 4 0 R]
+ /MediaBox [0 0 200 200]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Resources <<
+ /Font <<
+ /F1 5 0 R
+ /F2 6 0 R
+ >>
+ >>
+ /Contents 7 0 R
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Resources <<
+ /Font <<
+ /F1 5 0 R
+ /F2 6 0 R
+ >>
+ >>
+ /Contents 7 0 R
+>>
+endobj
+{{object 5 0}} <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Times-Roman
+>>
+endobj
+{{object 6 0}} <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Helvetica
+>>
+endobj
+{{object 7 0}} <<
+ {{streamlen}}
+>>
+stream
+BT
+20 50 Td
+/F1 12 Tf
+(Hello, world!) Tj
+0 50 Td
+/F2 16 Tf
+(Goodbye, world!) Tj
+ET
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/hello_world_2_pages.pdf b/testing/resources/hello_world_2_pages.pdf
new file mode 100644
index 0000000..4942e3a
--- /dev/null
+++ b/testing/resources/hello_world_2_pages.pdf
@@ -0,0 +1,81 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /MediaBox [0 0 200 200]
+ /Count 2
+ /Kids [3 0 R 4 0 R]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Resources <<
+ /Font <<
+ /F1 5 0 R
+ /F2 6 0 R
+ >>
+ >>
+ /Contents 7 0 R
+>>
+endobj
+4 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Resources <<
+ /Font <<
+ /F1 5 0 R
+ /F2 6 0 R
+ >>
+ >>
+ /Contents 7 0 R
+>>
+endobj
+5 0 obj <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Times-Roman
+>>
+endobj
+6 0 obj <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Helvetica
+>>
+endobj
+7 0 obj <<
+ /Length 83
+>>
+stream
+BT
+20 50 Td
+/F1 12 Tf
+(Hello, world!) Tj
+0 50 Td
+/F2 16 Tf
+(Goodbye, world!) Tj
+ET
+endstream
+endobj
+xref
+0 8
+0000000000 65535 f
+0000000015 00000 n
+0000000068 00000 n
+0000000163 00000 n
+0000000305 00000 n
+0000000447 00000 n
+0000000525 00000 n
+0000000601 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 8
+>>
+startxref
+735
+%%EOF
diff --git a/testing/resources/hello_world_2_pages_shared_resources_dict.in b/testing/resources/hello_world_2_pages_shared_resources_dict.in
new file mode 100644
index 0000000..9c30780
--- /dev/null
+++ b/testing/resources/hello_world_2_pages_shared_resources_dict.in
@@ -0,0 +1,64 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 2
+ /Kids [3 0 R 4 0 R]
+ /MediaBox [0 0 200 200]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Resources 5 0 R
+ /Contents 8 0 R
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Resources 5 0 R
+ /Contents 8 0 R
+>>
+endobj
+{{object 5 0}} <<
+ /Font <<
+ /F1 6 0 R
+ /F2 7 0 R
+ >>
+>>
+endobj
+{{object 6 0}} <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Times-Roman
+>>
+endobj
+{{object 7 0}} <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Helvetica
+>>
+endobj
+{{object 8 0}} <<
+ {{streamlen}}
+>>
+stream
+BT
+20 50 Td
+/F1 12 Tf
+(Hello, world!) Tj
+0 50 Td
+/F2 16 Tf
+(Goodbye, world!) Tj
+ET
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/hello_world_2_pages_shared_resources_dict.pdf b/testing/resources/hello_world_2_pages_shared_resources_dict.pdf
new file mode 100644
index 0000000..06b2531
--- /dev/null
+++ b/testing/resources/hello_world_2_pages_shared_resources_dict.pdf
@@ -0,0 +1,79 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /Count 2
+ /Kids [3 0 R 4 0 R]
+ /MediaBox [0 0 200 200]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Resources 5 0 R
+ /Contents 8 0 R
+>>
+endobj
+4 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Resources 5 0 R
+ /Contents 8 0 R
+>>
+endobj
+5 0 obj <<
+ /Font <<
+ /F1 6 0 R
+ /F2 7 0 R
+ >>
+>>
+endobj
+6 0 obj <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Times-Roman
+>>
+endobj
+7 0 obj <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Helvetica
+>>
+endobj
+8 0 obj <<
+ /Length 83
+>>
+stream
+BT
+20 50 Td
+/F1 12 Tf
+(Hello, world!) Tj
+0 50 Td
+/F2 16 Tf
+(Goodbye, world!) Tj
+ET
+endstream
+endobj
+xref
+0 9
+0000000000 65535 f
+0000000015 00000 n
+0000000068 00000 n
+0000000163 00000 n
+0000000251 00000 n
+0000000339 00000 n
+0000000404 00000 n
+0000000482 00000 n
+0000000558 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 9
+>>
+startxref
+692
+%%EOF
diff --git a/testing/resources/hello_world_2_pages_split_streams.in b/testing/resources/hello_world_2_pages_split_streams.in
new file mode 100644
index 0000000..ef68f5c
--- /dev/null
+++ b/testing/resources/hello_world_2_pages_split_streams.in
@@ -0,0 +1,75 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 2
+ /Kids [3 0 R 4 0 R]
+ /MediaBox [0 0 200 200]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Resources <<
+ /Font <<
+ /F1 5 0 R
+ /F2 6 0 R
+ >>
+ >>
+ /Contents 7 0 R
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Resources <<
+ /Font <<
+ /F1 5 0 R
+ /F2 6 0 R
+ >>
+ >>
+ /Contents 7 0 R
+>>
+endobj
+{{object 5 0}} <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Times-Roman
+>>
+endobj
+{{object 6 0}} <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Helvetica
+>>
+endobj
+{{object 7 0}} [8 0 R 9 0 R]
+{{object 8 0}} <<
+ {{streamlen}}
+>>
+stream
+BT
+20 50 Td
+/F1 12 Tf
+(Hello, world!) Tj
+endstream
+endobj
+{{object 9 0}} <<
+ {{streamlen}}
+>>
+stream
+BT
+20 100 Td
+/F2 16 Tf
+(Goodbye, world!) Tj
+ET
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/hello_world_2_pages_split_streams.pdf b/testing/resources/hello_world_2_pages_split_streams.pdf
new file mode 100644
index 0000000..3ac829f
--- /dev/null
+++ b/testing/resources/hello_world_2_pages_split_streams.pdf
@@ -0,0 +1,91 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /Count 2
+ /Kids [3 0 R 4 0 R]
+ /MediaBox [0 0 200 200]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Resources <<
+ /Font <<
+ /F1 5 0 R
+ /F2 6 0 R
+ >>
+ >>
+ /Contents 7 0 R
+>>
+endobj
+4 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Resources <<
+ /Font <<
+ /F1 5 0 R
+ /F2 6 0 R
+ >>
+ >>
+ /Contents 7 0 R
+>>
+endobj
+5 0 obj <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Times-Roman
+>>
+endobj
+6 0 obj <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Helvetica
+>>
+endobj
+7 0 obj [8 0 R 9 0 R]
+8 0 obj <<
+ /Length 41
+>>
+stream
+BT
+20 50 Td
+/F1 12 Tf
+(Hello, world!) Tj
+endstream
+endobj
+9 0 obj <<
+ /Length 47
+>>
+stream
+BT
+20 100 Td
+/F2 16 Tf
+(Goodbye, world!) Tj
+ET
+endstream
+endobj
+xref
+0 10
+0000000000 65535 f
+0000000015 00000 n
+0000000068 00000 n
+0000000163 00000 n
+0000000305 00000 n
+0000000447 00000 n
+0000000525 00000 n
+0000000601 00000 n
+0000000623 00000 n
+0000000715 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 10
+>>
+startxref
+813
+%%EOF
diff --git a/testing/resources/ink_annot.in b/testing/resources/ink_annot.in
new file mode 100644
index 0000000..da90b29
--- /dev/null
+++ b/testing/resources/ink_annot.in
@@ -0,0 +1,48 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 612 792]
+ /Annots [
+ 4 0 R 5 0 R
+ ]
+ /Tabs /R
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Annot
+ /Subtype /Ink
+ /NM (Ink-1)
+ /F 4
+ /InkList [[159 296 350 411 472 243.42]]
+ /P 3 0 R
+ /C [1 0.90196 0]
+ /Rect [293 530 349 542]
+>>
+endobj
+{{object 5 0}} <<
+ /Type /Annot
+ /Subtype /Ink
+ /NM (Ink-2)
+ /F 4
+ /InkList [[259 396 450 511 572 343 42]]
+ /P 3 0 R
+ /C [1 0.90196 0]
+ /Rect [293 530 349 542]
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/ink_annot.pdf b/testing/resources/ink_annot.pdf
new file mode 100644
index 0000000..306f28d
--- /dev/null
+++ b/testing/resources/ink_annot.pdf
@@ -0,0 +1,60 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 612 792]
+ /Annots [
+ 4 0 R 5 0 R
+ ]
+ /Tabs /R
+>>
+endobj
+4 0 obj <<
+ /Type /Annot
+ /Subtype /Ink
+ /NM (Ink-1)
+ /F 4
+ /InkList [[159 296 350 411 472 243.42]]
+ /P 3 0 R
+ /C [1 0.90196 0]
+ /Rect [293 530 349 542]
+>>
+endobj
+5 0 obj <<
+ /Type /Annot
+ /Subtype /Ink
+ /NM (Ink-2)
+ /F 4
+ /InkList [[259 396 450 511 572 343 42]]
+ /P 3 0 R
+ /C [1 0.90196 0]
+ /Rect [293 530 349 542]
+>>
+endobj
+xref
+0 6
+0000000000 65535 f
+0000000015 00000 n
+0000000068 00000 n
+0000000131 00000 n
+0000000251 00000 n
+0000000422 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 6
+>>
+startxref
+593
+%%EOF
diff --git a/testing/resources/javascript/annot_properties.in b/testing/resources/javascript/annot_properties.in
index 939365c..e143ac8 100644
--- a/testing/resources/javascript/annot_properties.in
+++ b/testing/resources/javascript/annot_properties.in
@@ -74,6 +74,15 @@
} catch (e) {
app.alert("ERROR: " + e);
}
+app.alert("Test setting empty name under changed name");
+try {
+ var annot = this.getAnnot(0, "nonesuch");
+ annot.name = "";
+ app.alert("name after empty name change: " + annot.name);
+} catch (e) {
+ app.alert("ERROR: " + e);
+}
+
endstream
endobj
{{object 22 0}} <<
diff --git a/testing/resources/javascript/annot_properties_expected.txt b/testing/resources/javascript/annot_properties_expected.txt
index 54548e6..252fd2b 100644
--- a/testing/resources/javascript/annot_properties_expected.txt
+++ b/testing/resources/javascript/annot_properties_expected.txt
@@ -14,3 +14,5 @@
Alert: SUCCESS: Document.getAnnot: Object no longer exists.
Alert: Test lookup under changed name
Alert: nonesuch after name change: object
+Alert: Test setting empty name under changed name
+Alert: name after empty name change:
diff --git a/testing/resources/javascript/app_methods.in b/testing/resources/javascript/app_methods.in
index 81fa660..1c01e84 100644
--- a/testing/resources/javascript/app_methods.in
+++ b/testing/resources/javascript/app_methods.in
@@ -19,6 +19,7 @@
/Parent 2 0 R
/MediaBox [0 0 612 792]
>>
+endobj
% OpenAction action
{{object 10 0}} <<
/Type /Action
@@ -26,7 +27,7 @@
/JS 11 0 R
>>
endobj
-% JS program to exexute
+% JS program to execute
{{object 11 0}} <<
{{streamlen}}
>>
@@ -34,7 +35,8 @@
{{include expect.js}}
try {
- expect("app.alert('message', 1, 2, 'title')", 0);
+ // Test unicode support, no particular reason for these CJK characters.
+ expect("app.alert('message \u4023', 1, 2, 'title \u4024')", 0);
expect("app.alert({'cMsg': 'message', 'cTitle': 'title'})", 0);
expect("app.alert({'cMsg': 'message', 'cTitle': 'title', 'nIcon': 3, 'nType': 4})", 0);
expect("app.alert(undefined)", 0);
diff --git a/testing/resources/javascript/app_methods_expected.txt b/testing/resources/javascript/app_methods_expected.txt
index 015ff28..86fb3a9 100644
--- a/testing/resources/javascript/app_methods_expected.txt
+++ b/testing/resources/javascript/app_methods_expected.txt
@@ -1,5 +1,5 @@
-title[icon=1,type=2]: message
-Alert: PASS: app.alert('message', 1, 2, 'title') = 0
+title 䀤[icon=1,type=2]: message 䀣
+Alert: PASS: app.alert('message 䀣', 1, 2, 'title 䀤') = 0
title: message
Alert: PASS: app.alert({'cMsg': 'message', 'cTitle': 'title'}) = 0
title[icon=3,type=4]: message
diff --git a/testing/resources/javascript/app_properties.in b/testing/resources/javascript/app_properties.in
index 1355e64..290fc24 100644
--- a/testing/resources/javascript/app_properties.in
+++ b/testing/resources/javascript/app_properties.in
@@ -25,6 +25,7 @@
>>
/MediaBox [0 0 612 792]
>>
+endobj
% Page number 1.
{{object 4 0}} <<
/Type /Page
@@ -34,6 +35,7 @@
>>
/MediaBox [0 0 612 792]
>>
+endobj
% Page number 2.
{{object 5 0}} <<
/Type /Page
@@ -43,6 +45,7 @@
>>
/MediaBox [0 0 612 792]
>>
+endobj
% Page number 3.
{{object 6 0}} <<
/Type /Page
@@ -52,6 +55,7 @@
>>
/MediaBox [0 0 612 792]
>>
+endobj
% Info
{{object 9 0}} <<
@@ -66,7 +70,7 @@
/JS 11 0 R
>>
endobj
-% JS program to exexute
+% JS program to execute
{{object 11 0}} <<
{{streamlen}}
>>
diff --git a/testing/resources/javascript/apply.in b/testing/resources/javascript/apply.in
index d944e83..e8ec5e2 100644
--- a/testing/resources/javascript/apply.in
+++ b/testing/resources/javascript/apply.in
@@ -23,6 +23,7 @@
/Contents [21 0 R]
/MediaBox [0 0 612 792]
>>
+endobj
% OpenAction action
{{object 10 0}} <<
/Type /Action
@@ -30,7 +31,7 @@
/JS 11 0 R
>>
endobj
-% JS program to exexute
+% JS program to execute
{{object 11 0}} <<
{{streamlen}}
>>
diff --git a/testing/resources/javascript/array_buffer.in b/testing/resources/javascript/array_buffer.in
index 06371ba..3790c6c 100644
--- a/testing/resources/javascript/array_buffer.in
+++ b/testing/resources/javascript/array_buffer.in
@@ -23,6 +23,7 @@
/Contents [21 0 R]
/MediaBox [0 0 612 792]
>>
+endobj
% OpenAction action
{{object 10 0}} <<
/Type /Action
@@ -30,7 +31,7 @@
/JS 11 0 R
>>
endobj
-% JS program to exexute
+% JS program to execute
{{object 11 0}} <<
{{streamlen}}
>>
diff --git a/testing/resources/javascript/bug_1098213.in b/testing/resources/javascript/bug_1098213.in
new file mode 100644
index 0000000..0c99311
--- /dev/null
+++ b/testing/resources/javascript/bug_1098213.in
@@ -0,0 +1,65 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /OpenAction 8 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 2
+ /Kids [
+ 3 0 R
+ 4 0 R
+ ]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /CropBox [179 173 200 75]
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /CropBox [127 214 186 29]
+ /Annots [
+ 5 0 R
+ 6 0 R
+ 7 0 R
+ ]
+>>
+endobj
+{{object 5 0}} <<
+ /Type /Annot
+>
+endobj
+{{object 6 0}} <<
+ /Type /Annot
+>>
+endobj
+{{object 7 0}} <<
+ /Type /Annot
+>>
+endobj
+{{object 8 0}} <<
+ /Type /Action
+ /S /JavaScript
+ /JS (
+ Object.defineProperty(Array.prototype, 1, {
+ set: (v) => {
+ Object.defineProperty(Array.prototype, 1, {
+ get: () => {}
+ });
+ }
+ });
+ try { this.getAnnots(); } catch (e) { app.alert('Caught: ' + e); }
+ app.alert('Done');
+ )
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/bug_1098213_expected.txt b/testing/resources/javascript/bug_1098213_expected.txt
new file mode 100644
index 0000000..9de1818
--- /dev/null
+++ b/testing/resources/javascript/bug_1098213_expected.txt
@@ -0,0 +1 @@
+Alert: Done
diff --git a/testing/resources/javascript/bug_1142688.in b/testing/resources/javascript/bug_1142688.in
new file mode 100644
index 0000000..6d3825d
--- /dev/null
+++ b/testing/resources/javascript/bug_1142688.in
@@ -0,0 +1,78 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 5 0 R
+ /AcroForm <<
+ /Fields [
+ 3 0 R
+ 2 0 R
+ ]
+ >>
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Tx
+ /T (tf1)
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Tx
+ /T (tf0)
+ /AA <<
+ /F 10 0 R
+ >>
+>>
+endobj
+{{object 5 0}} <<
+ /Type /Pages
+ /Count 2
+ /Kids [
+ 8 0 R
+ 9 0 R
+ ]
+>>
+endobj
+{{object 8 0}} <<
+ /Type /Page
+ /Parent 5 0 R
+ /Annots [3 0 R]
+>>
+endobj
+{{object 9 0}} <<
+ /Type /Page
+ /Parent 5 0 R
+ /Annots [2 0 R]
+>>
+endobj
+{{object 10 0}} <<
+ /Type /Action
+ /S /JavaScript
+ /JS 13 0 R
+>>
+{{object 13 0}} <<
+ {{streamlen}}
+>>
+stream
+function f3() {
+ // Setup dubious values in event recorder.
+ try { AFSpecial_Format(2); } catch(e) {}
+ try { AFNumber_Format(-302907477,0,1,-6,"",true); } catch(e) {}
+
+ // Exhaust call stack, then do work upon exiting each frame. The
+ // objective is to get any call() made under the covers to throw
+ // with a stack size exception.
+ try { f3(); } catch(e) {}
+ try { AFDate_Keystroke("yymm-dd"); } catch(e) {}
+}
+f3();
+app.alert('Done.');
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/bug_1142688_expected.txt b/testing/resources/javascript/bug_1142688_expected.txt
new file mode 100644
index 0000000..7cb32ca
--- /dev/null
+++ b/testing/resources/javascript/bug_1142688_expected.txt
@@ -0,0 +1 @@
+Alert: Done.
diff --git a/testing/resources/javascript/bug_1314658.in b/testing/resources/javascript/bug_1314658.in
new file mode 100644
index 0000000..f05b3ed
--- /dev/null
+++ b/testing/resources/javascript/bug_1314658.in
@@ -0,0 +1,29 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /]
+ /Subtype /Widget
+ /T (0)
+ /FT /Ch
+ /AA <<
+ /F 5 0 R
+ >>
+ /Annots 4 0 R
+4 0 obj [3 0 R /Ff 393216]
+ /AP<</N<<>>
+stream
+{{object 5 0}} <<
+ /JS (app.alert('done'))
+>>
+endobj
+{{trailer}}
+%%EOF
diff --git a/testing/resources/javascript/bug_1314658_expected.txt b/testing/resources/javascript/bug_1314658_expected.txt
new file mode 100644
index 0000000..daa1eca
--- /dev/null
+++ b/testing/resources/javascript/bug_1314658_expected.txt
@@ -0,0 +1 @@
+Alert: done
diff --git a/testing/resources/javascript/bug_1335681.in b/testing/resources/javascript/bug_1335681.in
new file mode 100644
index 0000000..254e596
--- /dev/null
+++ b/testing/resources/javascript/bug_1335681.in
@@ -0,0 +1,38 @@
+{{header}}
+{{object 1 0}} <<
+ /Pages 1 0 R
+ /OpenAction 2 0 R
+ /Names <<
+ /Dests 3 0 R
+ >>
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Action
+ /S /JavaScript
+ /JS (
+ app.alert\("Starting"\);
+ this.gotoNamedDest\(""\);
+ )
+>>
+endobj
+{{object 3 0}} <<
+ /Kids 4 0 R
+>>
+endobj
+{{object 4 0}} [
+ << >>
+ << >>
+ <<
+ /Kids [
+ <<
+ /Limits 4 0 R
+ >>
+ ]
+ >>
+]
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/bug_1335681_expected.txt b/testing/resources/javascript/bug_1335681_expected.txt
new file mode 100644
index 0000000..80a6951
--- /dev/null
+++ b/testing/resources/javascript/bug_1335681_expected.txt
@@ -0,0 +1 @@
+Alert: Starting
diff --git a/testing/resources/javascript/bug_1358075.in b/testing/resources/javascript/bug_1358075.in
new file mode 100644
index 0000000..b503bf2
--- /dev/null
+++ b/testing/resources/javascript/bug_1358075.in
@@ -0,0 +1,39 @@
+{{header}}
+{{object 1 0}} <<
+ /Pages 1 0 R
+ /OpenAction 2 0 R
+ /Names <<
+ /Dests 3 0 R
+ >>
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Action
+ /S /JavaScript
+ /JS (
+ this.gotoNamedDest\("2"\);
+ app.alert\("completed"\);
+ )
+>>
+endobj
+{{object 3 0}} <<
+ /Kids 4 0 R
+>>
+endobj
+{{object 4 0}} [
+ (1)
+ (3)
+ <<
+ /Kids [
+ <<
+ /Limits 4 0 R
+ /Names [(2) []]
+ >>
+ ]
+ >>
+]
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/bug_1358075_expected.txt b/testing/resources/javascript/bug_1358075_expected.txt
new file mode 100644
index 0000000..13d460b
--- /dev/null
+++ b/testing/resources/javascript/bug_1358075_expected.txt
@@ -0,0 +1 @@
+Alert: completed
diff --git a/testing/resources/javascript/bug_1445426.evt b/testing/resources/javascript/bug_1445426.evt
new file mode 100644
index 0000000..265e85b
--- /dev/null
+++ b/testing/resources/javascript/bug_1445426.evt
@@ -0,0 +1,3 @@
+mousedown,left,202,697
+mouseup,left,202,697
+keycode,40
diff --git a/testing/resources/javascript/bug_1445426.in b/testing/resources/javascript/bug_1445426.in
new file mode 100644
index 0000000..1483da7
--- /dev/null
+++ b/testing/resources/javascript/bug_1445426.in
@@ -0,0 +1,114 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /AcroForm 4 0 R
+ /OpenAction 40 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 2
+ /Kids [
+ 32 0 R
+ 34 0 R
+ ]
+>>
+endobj
+% Forms
+{{object 4 0}} <<
+ /Fields [
+ 10 0 R
+ 11 0 R
+ ]
+>>
+endobj
+% Fields
+{{object 10 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Tx
+ /T (Field_TextEdit)
+ /Rect [0 0 612 792]
+>>
+{{object 11 0}} <<
+ /T (Field_ComboBox)
+ /Parent 4 0 R
+ /Kids [12 0 R]
+ /Opt [(a) (b) (c) (d)]
+ /V [(a)]
+>>
+endobj
+{{object 12 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Ch
+ /Ff 131072
+ /Parent 11 0 R
+ /Kids [13 0 R]
+>>
+endobj
+{{object 13 0}} <<
+ /Parent 12 0 R
+ /Type /Annot
+ /Subtype /Widget
+ /Rect [0 0 612 792]
+ /AA << /K 20 0 R >>
+>>
+endobj
+% Pages
+{{object 32 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 612 792]
+ /Annots [13 0 R]
+
+>>
+endobj
+{{object 34 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 612 792]
+ /Annots [10 0 R]
+>>
+endobj
+% Document JS Action
+{{object 40 0}} <<
+ /Type /Action
+ /S /JavaScript
+ /JS 41 0 R
+>>
+endobj
+% JS program to exexute
+{{object 41 0}} <<
+ {{streamlen}}
+>>
+stream
+var field_text = this.getField("Field_TextEdit");
+var field_combobox = this.getField("Field_ComboBox");
+field_combobox.setFocus();
+this.__defineGetter__("filesize", function new_getter(){
+ field_text.setFocus();
+ field_combobox.borderStyle="dashed";
+ field_combobox.setFocus();
+ });
+endstream
+endobj
+% OpenAction action
+{{object 20 0}} <<
+ /S /JavaScript
+ /JS 21 0 R
+>>
+endobj
+% JS program to exexute
+{{object 21 0}} <<
+ {{streamlen}}
+>>
+stream
+var t = this.filesize;
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/bug_361.in b/testing/resources/javascript/bug_361.in
index 4af86a6..386142a 100644
--- a/testing/resources/javascript/bug_361.in
+++ b/testing/resources/javascript/bug_361.in
@@ -24,10 +24,12 @@
/Contents [21 0 R]
/MediaBox [0 0 612 792]
>>
+endobj
% Forms
{{object 4 0}} <<
/Fields [5 0 R]
>>
+endobj
% Field
{{object 5 0}} <<
/FT /Tx
@@ -36,6 +38,7 @@
/Subtype /Widget
/Rect [100 200 150 250]
>>
+endobj
% OpenAction action
{{object 10 0}} <<
/Type /Action
@@ -43,7 +46,7 @@
/JS 11 0 R
>>
endobj
-% JS program to exexute
+% JS program to execute
{{object 11 0}} <<
{{streamlen}}
>>
diff --git a/testing/resources/javascript/bug_492_1.in b/testing/resources/javascript/bug_492_1.in
index 9d48ad5..4b8026f 100644
--- a/testing/resources/javascript/bug_492_1.in
+++ b/testing/resources/javascript/bug_492_1.in
@@ -50,7 +50,7 @@
/JS 21 0 R
>>
endobj
-% JS program to exexute
+% JS program to execute
{{object 21 0}} <<
{{streamlen}}
>>
diff --git a/testing/resources/javascript/bug_494057.in b/testing/resources/javascript/bug_494057.in
index bbfa13e..3615d03 100644
--- a/testing/resources/javascript/bug_494057.in
+++ b/testing/resources/javascript/bug_494057.in
@@ -87,7 +87,7 @@
/JS 11 0 R
>>
endobj
-% JS program to exexute
+% JS program to execute
{{object 11 0}} <<
{{streamlen}}
>>
diff --git a/testing/resources/javascript/bug_679642.in b/testing/resources/javascript/bug_679642.in
index 644a259..b63cb70 100644
--- a/testing/resources/javascript/bug_679642.in
+++ b/testing/resources/javascript/bug_679642.in
@@ -86,7 +86,7 @@
/JS 11 0 R
>>
endobj
-% JS program to exexute
+% JS program to execute
{{object 11 0}} <<
{{streamlen}}
>>
diff --git a/testing/resources/javascript/bug_679643.in b/testing/resources/javascript/bug_679643.in
index c60fc0f..5ebe312 100644
--- a/testing/resources/javascript/bug_679643.in
+++ b/testing/resources/javascript/bug_679643.in
@@ -86,7 +86,7 @@
/JS 11 0 R
>>
endobj
-% JS program to exexute
+% JS program to execute
{{object 11 0}} <<
{{streamlen}}
>>
diff --git a/testing/resources/javascript/bug_695826.in b/testing/resources/javascript/bug_695826.in
index e17eaf1..9d32be7 100644
--- a/testing/resources/javascript/bug_695826.in
+++ b/testing/resources/javascript/bug_695826.in
@@ -23,6 +23,7 @@
/Contents [21 0 R]
/MediaBox [0 0 612 792]
>>
+endobj
% OpenAction action
{{object 10 0}} <<
/Type /Action
@@ -30,7 +31,7 @@
/JS 11 0 R
>>
endobj
-% JS program to exexute
+% JS program to execute
{{object 11 0}} <<
{{streamlen}}
>>
diff --git a/testing/resources/javascript/bug_735912.in b/testing/resources/javascript/bug_735912.in
index 06bd9a3..f3db5a2 100644
--- a/testing/resources/javascript/bug_735912.in
+++ b/testing/resources/javascript/bug_735912.in
@@ -53,7 +53,7 @@
/JS 31 0 R
>>
endobj
-% JS program to exexute
+% JS program to execute
{{object 31 0}} <<
{{streamlen}}
>>
@@ -69,7 +69,7 @@
/JS 35 0 R
>>
endobj
-% JS program to exexute
+% JS program to execute
{{object 35 0}} <<
{{streamlen}}
>>
@@ -85,7 +85,7 @@
/JS 37 0 R
>>
endobj
-% JS program to exexute
+% JS program to execute
{{object 37 0}} <<
{{streamlen}}
>>
diff --git a/testing/resources/javascript/bug_740166.in b/testing/resources/javascript/bug_740166.in
index 425374d..6f0c829 100644
--- a/testing/resources/javascript/bug_740166.in
+++ b/testing/resources/javascript/bug_740166.in
@@ -24,10 +24,12 @@
/Contents [21 0 R]
/MediaBox [0 0 612 792]
>>
+endobj
% Forms
{{object 4 0}} <<
/Fields [5 0 R]
>>
+endobj
% Field
{{object 5 0}} <<
/FT /Tx
@@ -36,6 +38,7 @@
/Subtype /Widget
/Rect [100 200 150 250]
>>
+endobj
% OpenAction action
{{object 10 0}} <<
/Type /Action
@@ -43,7 +46,7 @@
/JS 11 0 R
>>
endobj
-% JS program to exexute
+% JS program to execute
{{object 11 0}} <<
{{streamlen}}
>>
diff --git a/testing/resources/javascript/bug_959274_1.in b/testing/resources/javascript/bug_959274_1.in
index 74909c0..9efb9dc 100644
--- a/testing/resources/javascript/bug_959274_1.in
+++ b/testing/resources/javascript/bug_959274_1.in
@@ -23,6 +23,7 @@
/Contents [21 0 R]
/MediaBox [0 0 612 792]
>>
+endobj
% OpenAction action
{{object 10 0}} <<
/Type /Action
diff --git a/testing/resources/javascript/color_methods.in b/testing/resources/javascript/color_methods.in
index bf03bb4..44afced 100644
--- a/testing/resources/javascript/color_methods.in
+++ b/testing/resources/javascript/color_methods.in
@@ -27,7 +27,7 @@
/JS 11 0 R
>>
endobj
-% JS program to exexute
+% JS program to execute
{{object 11 0}} <<
{{streamlen}}
>>
diff --git a/testing/resources/javascript/color_properties.in b/testing/resources/javascript/color_properties.in
index 7d09656..0fea81e 100644
--- a/testing/resources/javascript/color_properties.in
+++ b/testing/resources/javascript/color_properties.in
@@ -20,6 +20,7 @@
/Contents [21 0 R]
/MediaBox [0 0 612 792]
>>
+endobj
% OpenAction action
{{object 10 0}} <<
/Type /Action
@@ -27,7 +28,7 @@
/JS 11 0 R
>>
endobj
-% JS program to exexute
+% JS program to execute
{{object 11 0}} <<
{{streamlen}}
>>
diff --git a/testing/resources/javascript/console_methods.in b/testing/resources/javascript/console_methods.in
index 0fbba3f..0dc74e3 100644
--- a/testing/resources/javascript/console_methods.in
+++ b/testing/resources/javascript/console_methods.in
@@ -27,7 +27,7 @@
/JS 11 0 R
>>
endobj
-% JS program to exexute
+% JS program to execute
{{object 11 0}} <<
{{streamlen}}
>>
diff --git a/testing/resources/javascript/constructor.in b/testing/resources/javascript/constructor.in
index 1211f89..90c41c0 100644
--- a/testing/resources/javascript/constructor.in
+++ b/testing/resources/javascript/constructor.in
@@ -19,6 +19,7 @@
/Parent 2 0 R
/MediaBox [0 0 612 792]
>>
+endobj
% OpenAction action
{{object 10 0}} <<
/Type /Action
@@ -26,34 +27,18 @@
/JS 11 0 R
>>
endobj
-% JS program to exexute
+% JS program to execute
{{object 11 0}} <<
{{streamlen}}
>>
stream
-function testIllegalConstructor(name, allowed) {
- const constructorString = name + ".constructor";
- let constructor;
- try {
- constructor = eval(constructorString);
- } catch (e) {
- app.alert("FAIL: No such " + constructorString);
- return;
- }
- try {
- constructor();
- app.alert("FAIL: " + constructorString + "(): returned");
- } catch (e) {
- app.alert("PASS: " + constructorString + "(): " + e);
- }
- try {
- new constructor;
- app.alert("FAIL: new " + constructorString + ": returned");
- } catch (e) {
- app.alert("PASS: new " + constructorString + ": " + e);
- }
-}
+{{include constructor.js}}
+
+// Global objects
testIllegalConstructor("this");
+testIllegalConstructor("globalThis");
+
+// Static objects
testIllegalConstructor("app");
testIllegalConstructor("event");
testIllegalConstructor("font");
@@ -69,6 +54,11 @@
testIllegalConstructor("zoomtype");
testIllegalConstructor("scaleHow");
testIllegalConstructor("scaleWhen");
+
+// Dynamic objects
+timer1 = app.setTimeOut("var marked = true", 1000);
+testLegalConstructor("timer1");
+
endstream
endobj
{{xref}}
diff --git a/testing/resources/javascript/constructor.js b/testing/resources/javascript/constructor.js
new file mode 100644
index 0000000..f8c98aa
--- /dev/null
+++ b/testing/resources/javascript/constructor.js
@@ -0,0 +1,49 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+function testLegalConstructor(name, allowed) {
+ const constructorString = name + ".constructor";
+ var constructor;
+ try {
+ constructor = eval(constructorString);
+ } catch (e) {
+ app.alert("FAIL: No such " + constructorString);
+ return;
+ }
+ try {
+ constructor();
+ app.alert("FAIL: " + constructorString + "(): returned");
+ } catch (e) {
+ app.alert("PASS: " + constructorString + "(): " + e);
+ }
+ try {
+ var thing = new constructor;
+ app.alert("PASS: new " + constructorString + ": " + thing);
+ } catch (e) {
+ app.alert("FAIL: new " + constructorString + ": " + e);
+ }
+}
+
+function testIllegalConstructor(name, allowed) {
+ const constructorString = name + ".constructor";
+ var constructor;
+ try {
+ constructor = eval(constructorString);
+ } catch (e) {
+ app.alert("FAIL: No such " + constructorString);
+ return;
+ }
+ try {
+ constructor();
+ app.alert("FAIL: " + constructorString + "(): returned");
+ } catch (e) {
+ app.alert("PASS: " + constructorString + "(): " + e);
+ }
+ try {
+ new constructor;
+ app.alert("FAIL: new " + constructorString + ": returned");
+ } catch (e) {
+ app.alert("PASS: new " + constructorString + ": " + e);
+ }
+}
diff --git a/testing/resources/javascript/constructor_expected.txt b/testing/resources/javascript/constructor_expected.txt
index af03337..8c58aad 100644
--- a/testing/resources/javascript/constructor_expected.txt
+++ b/testing/resources/javascript/constructor_expected.txt
@@ -1,5 +1,7 @@
Alert: PASS: this.constructor(): illegal constructor
Alert: PASS: new this.constructor: not a dynamic object
+Alert: PASS: globalThis.constructor(): illegal constructor
+Alert: PASS: new globalThis.constructor: not a dynamic object
Alert: PASS: app.constructor(): illegal constructor
Alert: PASS: new app.constructor: not a dynamic object
Alert: PASS: event.constructor(): illegal constructor
@@ -30,3 +32,5 @@
Alert: PASS: new scaleHow.constructor: not a dynamic object
Alert: PASS: scaleWhen.constructor(): illegal constructor
Alert: PASS: new scaleWhen.constructor: not a dynamic object
+Alert: PASS: timer1.constructor(): illegal constructor
+Alert: PASS: new timer1.constructor: [object Object]
diff --git a/testing/resources/javascript/consts.in b/testing/resources/javascript/consts.in
index dbfd579..b4180ff 100644
--- a/testing/resources/javascript/consts.in
+++ b/testing/resources/javascript/consts.in
@@ -23,6 +23,7 @@
/Contents [21 0 R]
/MediaBox [0 0 612 792]
>>
+endobj
% OpenAction action
{{object 10 0}} <<
/Type /Action
@@ -30,7 +31,7 @@
/JS 11 0 R
>>
endobj
-% JS program to exexute
+% JS program to execute
{{object 11 0}} <<
{{streamlen}}
>>
diff --git a/testing/resources/javascript/document_methods.in b/testing/resources/javascript/document_methods.in
index 9571e55..5db9775 100644
--- a/testing/resources/javascript/document_methods.in
+++ b/testing/resources/javascript/document_methods.in
@@ -26,6 +26,7 @@
/MediaBox [0 0 612 792]
/Contents 8 0 R
>>
+endobj
% Page number 1.
{{object 4 0}} <<
/Type /Page
@@ -35,6 +36,7 @@
>>
/MediaBox [0 0 612 792]
>>
+endobj
% Page number 2.
{{object 5 0}} <<
/Type /Page
@@ -44,6 +46,7 @@
>>
/MediaBox [0 0 612 792]
>>
+endobj
% Page number 3.
{{object 6 0}} <<
/Type /Page
@@ -53,6 +56,7 @@
>>
/MediaBox [0 0 612 792]
>>
+endobj
% Contents of the page.
{{object 8 0}} <<
{{streamlen}}
@@ -78,7 +82,7 @@
/JS 11 0 R
>>
endobj
-% JS program to exexute
+% JS program to execute
{{object 11 0}} <<
{{streamlen}}
>>
diff --git a/testing/resources/javascript/document_properties.in b/testing/resources/javascript/document_properties.in
index fa7d92a..44a108b 100644
--- a/testing/resources/javascript/document_properties.in
+++ b/testing/resources/javascript/document_properties.in
@@ -25,6 +25,7 @@
>>
/MediaBox [0 0 612 792]
>>
+endobj
% Page number 1.
{{object 4 0}} <<
/Type /Page
@@ -34,6 +35,7 @@
>>
/MediaBox [0 0 612 792]
>>
+endobj
% Page number 2.
{{object 5 0}} <<
/Type /Page
@@ -43,6 +45,7 @@
>>
/MediaBox [0 0 612 792]
>>
+endobj
% Page number 3.
{{object 6 0}} <<
/Type /Page
@@ -52,6 +55,7 @@
>>
/MediaBox [0 0 612 792]
>>
+endobj
% Info
{{object 9 0}} <<
@@ -66,7 +70,7 @@
/JS 11 0 R
>>
endobj
-% JS program to exexute
+% JS program to execute
{{object 11 0}} <<
{{streamlen}}
>>
diff --git a/testing/resources/javascript/document_properties_expected.txt b/testing/resources/javascript/document_properties_expected.txt
index 5bab82f..ba89da0 100644
--- a/testing/resources/javascript/document_properties_expected.txt
+++ b/testing/resources/javascript/document_properties_expected.txt
@@ -237,13 +237,13 @@
Alert: this.zoomType = 3; yields 3
Alert: *** Getting properties ***
Alert: this.ADBE is undefined undefined
-Alert: this.author is string 3
+Alert: this.author is string Joe Random Author
Alert: this.baseURL is string 3
Alert: this.bookmarkRoot is undefined undefined
Alert: this.calculate is boolean true
Alert: this.Collab is undefined undefined
-Alert: this.creationDate is string 3
-Alert: this.creator is string 3
+Alert: this.creationDate is string
+Alert: this.creator is string Joe Random Creator
Alert: this.delay is boolean true
Alert: this.dirty is boolean true
Alert: this.documentFileName is string
@@ -251,10 +251,10 @@
Alert: this.filesize is number 0
Alert: this.icons is undefined undefined
Alert: this.info is object [object Object]
-Alert: this.keywords is string 3
+Alert: this.keywords is string
Alert: this.layout is undefined undefined
Alert: this.media is undefined undefined
-Alert: this.modDate is string 3
+Alert: this.modDate is string
Alert: this.mouseX is undefined undefined
Alert: this.mouseY is undefined undefined
Alert: this.numFields is number 0
@@ -262,9 +262,9 @@
Alert: this.pageNum is undefined undefined
Alert: this.pageWindowRect is undefined undefined
Alert: this.path is string /myfile.pdf
-Alert: this.producer is string 3
-Alert: this.subject is string 3
-Alert: this.title is string 3
+Alert: this.producer is string
+Alert: this.subject is string
+Alert: this.title is string
Alert: this.URL is string myfile.pdf
Alert: this.zoom is undefined undefined
Alert: this.zoomType is undefined undefined
diff --git a/testing/resources/javascript/expect.js b/testing/resources/javascript/expect.js
index 832f1d6..a7a5643 100644
--- a/testing/resources/javascript/expect.js
+++ b/testing/resources/javascript/expect.js
@@ -1,3 +1,7 @@
+// Copyright 2022 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
function expect(expression, expected) {
try {
var actual = eval(expression);
diff --git a/testing/resources/javascript/field.fragment b/testing/resources/javascript/field.fragment
index 335e1ae..ff8f7f4 100644
--- a/testing/resources/javascript/field.fragment
+++ b/testing/resources/javascript/field.fragment
@@ -46,6 +46,7 @@
10 0 R
11 0 R
12 0 R
+ 13 0 R
]
>>
endobj
@@ -68,6 +69,9 @@
/Parent 5 0 R
/T (MyPushButton)
/Rect [220 221 240 241]
+ /MK <<
+ /TP 4
+ >>
>>
endobj
{{object 8 0}} <<
@@ -132,6 +136,20 @@
/V ("/path/to/file.pdf")
>>
endobj
+% Questionable TP value.
+{{object 13 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Btn
+ /Ff 65536
+ /Parent 5 0 R
+ /T (MyBadPushButton)
+ /Rect [400 421 440 441]
+ /MK <<
+ /TP 7
+ >>
+>>
+endobj
% OpenAction action
{{object 15 0}} <<
/Type /Action
diff --git a/testing/resources/javascript/field_methods.in b/testing/resources/javascript/field_methods.in
index 81b9eed..4684552 100644
--- a/testing/resources/javascript/field_methods.in
+++ b/testing/resources/javascript/field_methods.in
@@ -1,7 +1,7 @@
{{header}}
{{include field.fragment}}
-% JS program to exexute
+% JS program to execute
{{object 16 0}} <<
{{streamlen}}
>>
@@ -51,7 +51,18 @@
testGetArray();
expect("this.getField('MyField.MyPushButton').buttonGetCaption()", "");
+expect("this.getField('MyField.MyPushButton').buttonGetCaption(0)", "");
+expect("this.getField('MyField.MyPushButton').buttonGetCaption(1)", "");
+expect("this.getField('MyField.MyPushButton').buttonGetCaption(2)", "");
+expectError("this.getField('MyField.MyPushButton').buttonGetCaption(3)")
+expectError("this.getField('MyField.MyMultiSelect').buttonGetCaption()")
+
expect("this.getField('MyField.MyPushButton').buttonGetIcon()", "[object Object]");
+expect("this.getField('MyField.MyPushButton').buttonGetIcon(0)", "[object Object]");
+expect("this.getField('MyField.MyPushButton').buttonGetIcon(1)", "[object Object]");
+expectError("this.getField('MyField.MyPushButton').buttonGetIcon(3)");
+expectError("this.getField('MyField.MyMultiSelect').buttonGetIcon()");
+
expect("this.getField('MyField.MyPushButton').buttonImportIcon()", undefined);
expect("this.getField('MyField.MyFile').browseForFileToSubmit()", undefined);
diff --git a/testing/resources/javascript/field_methods_expected.txt b/testing/resources/javascript/field_methods_expected.txt
index 8d7ec84..99dbf09 100644
--- a/testing/resources/javascript/field_methods_expected.txt
+++ b/testing/resources/javascript/field_methods_expected.txt
@@ -8,7 +8,8 @@
Alert: dotdot1 is
Alert: dotdot2 is MyField.MyPushButton
Alert: dotdot3 is MyField
-Alert: found 7 sub-fields:
+Alert: found 8 sub-fields:
+Alert: MyField.MyBadPushButton
Alert: MyField.MyCheckBox
Alert: MyField.MyFile
Alert: MyField.MyMultiSelect
@@ -17,7 +18,16 @@
Alert: MyField.MySingleSelect
Alert: MyField.MyText
Alert: PASS: this.getField('MyField.MyPushButton').buttonGetCaption() =
+Alert: PASS: this.getField('MyField.MyPushButton').buttonGetCaption(0) =
+Alert: PASS: this.getField('MyField.MyPushButton').buttonGetCaption(1) =
+Alert: PASS: this.getField('MyField.MyPushButton').buttonGetCaption(2) =
+Alert: PASS: this.getField('MyField.MyPushButton').buttonGetCaption(3) threw Field.buttonGetCaption: Incorrect parameter value.
+Alert: PASS: this.getField('MyField.MyMultiSelect').buttonGetCaption() threw Field.buttonGetCaption: Object is of the wrong type.
Alert: PASS: this.getField('MyField.MyPushButton').buttonGetIcon() = [object Object]
+Alert: PASS: this.getField('MyField.MyPushButton').buttonGetIcon(0) = [object Object]
+Alert: PASS: this.getField('MyField.MyPushButton').buttonGetIcon(1) = [object Object]
+Alert: PASS: this.getField('MyField.MyPushButton').buttonGetIcon(3) threw Field.buttonGetIcon: Incorrect parameter value.
+Alert: PASS: this.getField('MyField.MyMultiSelect').buttonGetIcon() threw Field.buttonGetIcon: Object is of the wrong type.
Alert: PASS: this.getField('MyField.MyPushButton').buttonImportIcon() = undefined
Alert: PASS: this.getField('MyField.MyFile').browseForFileToSubmit() = undefined
Alert: PASS: this.getField('MyField.MyMultiSelect').getItemAt(0) = foo
diff --git a/testing/resources/javascript/field_properties.in b/testing/resources/javascript/field_properties.in
index 2126384..3887787 100644
--- a/testing/resources/javascript/field_properties.in
+++ b/testing/resources/javascript/field_properties.in
@@ -1,6 +1,6 @@
{{header}}
{{include field.fragment}}
-% JS program to exexute
+% JS program to execute
{{object 16 0}} <<
{{streamlen}}
>>
@@ -11,6 +11,7 @@
var field = this.getField("MyField");
var text = this.getField("MyField.MyText");
var button = this.getField("MyField.MyPushButton");
+ var badbutton = this.getField("MyField.MyBadPushButton");
var radio = this.getField("MyField.MyRadio");
var list = this.getField("MyField.MyMultiSelect");
var check = this.getField("MyField.MyCheckBox");
@@ -23,6 +24,7 @@
testFieldPropertiesCase(field);
testTextPropertiesCase(text);
testPushButtonPropertiesCase(button);
+ testBadPushButtonPropertiesCase(badbutton);
testRadioButtonPropertiesCase(radio);
testCheckBoxPropertiesCase(check);
testListBoxPropertiesCase(list);
@@ -68,7 +70,7 @@
testROProperty(field, "page", 0);
testRIProperty(field, "password", false, 42);
testRWProperty(field, "print", true, false);
- testRIProperty(field, "readonly", false, true);
+ testRWProperty(field, "readonly", false, true);
testROProperty(field, "rect", [200,221,220,201]);
// testROProperty(field, "required", "clams");
testRIProperty(field, "richText", false, true);
@@ -94,7 +96,7 @@
testRIProperty(field, "buttonAlignX", 0, 50);
testRIProperty(field, "buttonAlignY", 0, 50);
testRIProperty(field, "buttonFitBounds", false);
- testRIProperty(field, "buttonPosition", 0);
+ testRIProperty(field, "buttonPosition", 4);
testRIProperty(field, "buttonScaleHow", 0);
testRIProperty(field, "buttonScaleWhen", 0);
testRIProperty(field, "highlight", "invert");
@@ -104,11 +106,19 @@
}
}
+function testBadPushButtonPropertiesCase(field) {
+ try {
+ testRIProperty(field, "buttonPosition", 7); // not checked.
+ } catch (e) {
+ app.alert("Unexpected error: " + e);
+ }
+}
+
function testRadioButtonPropertiesCase(field) {
try {
- testROProperty(field, "exportValues", "N");
+ testROProperty(field, "exportValues", "Yes");
testRIProperty(field, "radiosInUnison", false);
- testRIProperty(field, "style", "check");
+ testRIProperty(field, "style", "circle");
testROProperty(field, "type", "radiobutton");
testRIProperty(field, "value", "Off");
testROProperty(field, "valueAsString", "Off");
@@ -119,7 +129,7 @@
function testCheckBoxPropertiesCase(field) {
try {
- testROProperty(field, "exportValues", "N");
+ testROProperty(field, "exportValues", "Yes");
testRIProperty(field, "style", "check");
testROProperty(field, "type", "checkbox");
testRIProperty(field, "value", "Off");
diff --git a/testing/resources/javascript/field_properties_expected.txt b/testing/resources/javascript/field_properties_expected.txt
index 3b2e6f6..2c57daf 100644
--- a/testing/resources/javascript/field_properties_expected.txt
+++ b/testing/resources/javascript/field_properties_expected.txt
@@ -51,7 +51,7 @@
Alert: PASS: print = true
Alert: PASS: print = false
Alert: PASS: readonly = false
-Alert: PASS: readonly = false
+Alert: PASS: readonly = true
Alert: PASS: rect = 200,221,220,201
Alert: PASS: rect threw Field.rect: Incorrect parameter value.
Alert: PASS: richText = false
@@ -84,8 +84,8 @@
Alert: PASS: buttonAlignY = 0
Alert: PASS: buttonFitBounds = false
Alert: PASS: buttonFitBounds = false
-Alert: PASS: buttonPosition = 0
-Alert: PASS: buttonPosition = 0
+Alert: PASS: buttonPosition = 4
+Alert: PASS: buttonPosition = 4
Alert: PASS: buttonScaleHow = false
Alert: PASS: buttonScaleHow = false
Alert: PASS: buttonScaleWhen = 0
@@ -94,19 +94,21 @@
Alert: PASS: highlight = invert
Alert: PASS: type = button
Alert: PASS: type threw Field.type: Operation not supported.
-Alert: PASS: exportValues = N
+Alert: PASS: buttonPosition = 7
+Alert: PASS: buttonPosition = 7
+Alert: PASS: exportValues = Yes
Alert: PASS: exportValues threw Field.exportValues: Object no longer exists.
Alert: PASS: radiosInUnison = false
Alert: PASS: radiosInUnison = false
-Alert: PASS: style = check
-Alert: PASS: style = check
+Alert: PASS: style = circle
+Alert: PASS: style = circle
Alert: PASS: type = radiobutton
Alert: PASS: type threw Field.type: Operation not supported.
Alert: PASS: value = Off
Alert: PASS: value = Off
Alert: PASS: valueAsString = Off
Alert: PASS: valueAsString threw Field.valueAsString: Operation not supported.
-Alert: PASS: exportValues = N
+Alert: PASS: exportValues = Yes
Alert: PASS: exportValues threw Field.exportValues: Object no longer exists.
Alert: PASS: style = check
Alert: PASS: style = check
diff --git a/testing/resources/javascript/foreground_task.in b/testing/resources/javascript/foreground_task.in
new file mode 100644
index 0000000..dd5444a
--- /dev/null
+++ b/testing/resources/javascript/foreground_task.in
@@ -0,0 +1,43 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /OpenAction 10 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [
+ 3 0 R
+ ]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 612 792]
+>>
+endobj
+{{object 10 0}} <<
+ /Type /Action
+ /S /JavaScript
+ /JS 11 0 R
+>>
+endobj
+{{object 11 0}} <<
+ {{streamlen}}
+>>
+stream
+try {
+ gc({type: 'major', execution: 'async'}).then(() => { app.alert('Resolved') });
+ app.alert('Posted');
+} catch (e) {
+ app.alert('Error: ' + e);
+}
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/foreground_task_expected.txt b/testing/resources/javascript/foreground_task_expected.txt
new file mode 100644
index 0000000..d40e100
--- /dev/null
+++ b/testing/resources/javascript/foreground_task_expected.txt
@@ -0,0 +1,2 @@
+Alert: Posted
+Alert: Resolved
diff --git a/testing/resources/javascript/globals.in b/testing/resources/javascript/globals.in
index de67392..bbca813 100644
--- a/testing/resources/javascript/globals.in
+++ b/testing/resources/javascript/globals.in
@@ -23,6 +23,7 @@
/Contents [21 0 R]
/MediaBox [0 0 612 792]
>>
+endobj
% OpenAction action
{{object 10 0}} <<
/Type /Action
@@ -30,7 +31,7 @@
/JS 11 0 R
>>
endobj
-% JS program to exexute
+% JS program to execute
{{object 11 0}} <<
{{streamlen}}
>>
@@ -58,7 +59,11 @@
// Test null and undefined.
{ "name": "null_var", "value": null },
- { "name": "undefined_var", "value": undefined }
+ { "name": "undefined_var", "value": undefined },
+
+ // Test to show unicode currently handled.
+ { "name": "unicode_var", "value": "\u4025\u4026_string" },
+ { "name": "\u4025\u4026_var", "value": "string" },
];
function setup_global() {
diff --git a/testing/resources/javascript/globals_expected.txt b/testing/resources/javascript/globals_expected.txt
index fcebd70..9e80a62 100644
--- a/testing/resources/javascript/globals_expected.txt
+++ b/testing/resources/javascript/globals_expected.txt
@@ -10,17 +10,20 @@
Alert: object_var = undefined
Alert: null_var = undefined
Alert: undefined_var = undefined
+Alert: unicode_var = undefined
+Alert: 䀥䀦_var = undefined
Alert: ************ After Setup ************
Alert: Enumerable Globals:
Alert: setPersistent = function setPersistent() { [native code] }, own property = true
-Alert: true_var = true, own property = true
Alert: false_var = false, own property = true
-Alert: zero_var = 0, own property = true
-Alert: number_var = -3.918, own property = true
-Alert: string_var = This is a string, own property = true
-Alert: object_var = [object Object], own property = true
Alert: null_var = null, own property = true
-Alert: undefined_var = undefined, own property = true
+Alert: number_var = -3.918, own property = true
+Alert: object_var = [object Object], own property = true
+Alert: string_var = This is a string, own property = true
+Alert: true_var = true, own property = true
+Alert: unicode_var = 䀥䀦_string, own property = true
+Alert: zero_var = 0, own property = true
+Alert: 䀥䀦_var = string, own property = true
Alert: Expected Globals:
Alert: true_var = true
Alert: false_var = false
@@ -33,6 +36,8 @@
Alert: blue
Alert: null_var = null
Alert: undefined_var = undefined
+Alert: unicode_var = 䀥䀦_string
+Alert: 䀥䀦_var = string
Alert: ************ After Deletion ************
Alert: Enumerable Globals:
Alert: setPersistent = function setPersistent() { [native code] }, own property = true
@@ -45,18 +50,21 @@
Alert: object_var = undefined
Alert: null_var = undefined
Alert: undefined_var = undefined
+Alert: unicode_var = undefined
+Alert: 䀥䀦_var = undefined
Alert: For undefined_var: Set Persistent: ERROR: global.setPersistent: Global value not found.
Alert: ************ After Setup and Persist false ************
Alert: Enumerable Globals:
Alert: setPersistent = function setPersistent() { [native code] }, own property = true
-Alert: true_var = true, own property = true
Alert: false_var = false, own property = true
-Alert: zero_var = 0, own property = true
-Alert: number_var = -3.918, own property = true
-Alert: string_var = This is a string, own property = true
-Alert: object_var = [object Object], own property = true
Alert: null_var = null, own property = true
-Alert: undefined_var = undefined, own property = true
+Alert: number_var = -3.918, own property = true
+Alert: object_var = [object Object], own property = true
+Alert: string_var = This is a string, own property = true
+Alert: true_var = true, own property = true
+Alert: unicode_var = 䀥䀦_string, own property = true
+Alert: zero_var = 0, own property = true
+Alert: 䀥䀦_var = string, own property = true
Alert: Expected Globals:
Alert: true_var = true
Alert: false_var = false
@@ -69,6 +77,8 @@
Alert: blue
Alert: null_var = null
Alert: undefined_var = undefined
+Alert: unicode_var = 䀥䀦_string
+Alert: 䀥䀦_var = string
Alert: For true_var: Set Persistent: ERROR: global.setPersistent: Global value not found.
Alert: For false_var: Set Persistent: ERROR: global.setPersistent: Global value not found.
Alert: For zero_var: Set Persistent: ERROR: global.setPersistent: Global value not found.
@@ -77,6 +87,8 @@
Alert: For object_var: Set Persistent: ERROR: global.setPersistent: Global value not found.
Alert: For null_var: Set Persistent: ERROR: global.setPersistent: Global value not found.
Alert: For undefined_var: Set Persistent: ERROR: global.setPersistent: Global value not found.
+Alert: For unicode_var: Set Persistent: ERROR: global.setPersistent: Global value not found.
+Alert: For 䀥䀦_var: Set Persistent: ERROR: global.setPersistent: Global value not found.
Alert: ************ After Delete and Persist ************
Alert: Enumerable Globals:
Alert: setPersistent = function setPersistent() { [native code] }, own property = true
@@ -89,18 +101,21 @@
Alert: object_var = undefined
Alert: null_var = undefined
Alert: undefined_var = undefined
+Alert: unicode_var = undefined
+Alert: 䀥䀦_var = undefined
Alert: For undefined_var: Set Persistent: ERROR: global.setPersistent: Global value not found.
Alert: ************ After Setup and Persist true ************
Alert: Enumerable Globals:
Alert: setPersistent = function setPersistent() { [native code] }, own property = true
-Alert: true_var = true, own property = true
Alert: false_var = false, own property = true
-Alert: zero_var = 0, own property = true
-Alert: number_var = -3.918, own property = true
-Alert: string_var = This is a string, own property = true
-Alert: object_var = [object Object], own property = true
Alert: null_var = null, own property = true
-Alert: undefined_var = undefined, own property = true
+Alert: number_var = -3.918, own property = true
+Alert: object_var = [object Object], own property = true
+Alert: string_var = This is a string, own property = true
+Alert: true_var = true, own property = true
+Alert: unicode_var = 䀥䀦_string, own property = true
+Alert: zero_var = 0, own property = true
+Alert: 䀥䀦_var = string, own property = true
Alert: Expected Globals:
Alert: true_var = true
Alert: false_var = false
@@ -113,3 +128,5 @@
Alert: blue
Alert: null_var = null
Alert: undefined_var = undefined
+Alert: unicode_var = 䀥䀦_string
+Alert: 䀥䀦_var = string
diff --git a/testing/resources/javascript/immutable_proto.in b/testing/resources/javascript/immutable_proto.in
new file mode 100644
index 0000000..61885c5
--- /dev/null
+++ b/testing/resources/javascript/immutable_proto.in
@@ -0,0 +1,43 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /OpenAction 10 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [
+ 3 0 R
+ ]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 612 792]
+>>
+endobj
+% OpenAction action
+{{object 10 0}} <<
+ /Type /Action
+ /S /JavaScript
+ /JS 11 0 R
+>>
+endobj
+{{object 11 0}} <<
+ {{streamlen}}
+>>
+stream
+{{include expect.js}}
+expect("this.__proto__", "[object Object]");
+expect("app.__proto__", "[object Object]");
+expectError("this.__proto__ = {}");
+expectError("app.__proto__ = this");
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/immutable_proto_expected.txt b/testing/resources/javascript/immutable_proto_expected.txt
new file mode 100644
index 0000000..7e7c670
--- /dev/null
+++ b/testing/resources/javascript/immutable_proto_expected.txt
@@ -0,0 +1,4 @@
+Alert: PASS: this.__proto__ = [object Object]
+Alert: PASS: app.__proto__ = [object Object]
+Alert: PASS: this.__proto__ = {} threw TypeError: Immutable prototype object '[object global]' cannot have their prototype set
+Alert: PASS: app.__proto__ = this threw TypeError: Immutable prototype object '[object Object]' cannot have their prototype set
diff --git a/testing/resources/javascript/mouse_events.in b/testing/resources/javascript/mouse_events.in
index 4c6935e..b7b5a6d 100644
--- a/testing/resources/javascript/mouse_events.in
+++ b/testing/resources/javascript/mouse_events.in
@@ -24,6 +24,7 @@
/Contents [21 0 R]
/MediaBox [0 0 612 792]
>>
+endobj
% Forms
{{object 4 0}} <<
/Fields [
@@ -32,6 +33,7 @@
7 0 R
]
>>
+endobj
% Field with actions:
% Cursor enter: E
% Cursor exit: X
@@ -54,6 +56,7 @@
/Bl 15 0 R
>>
>>
+endobj
{{object 6 0}} <<
/Type /Annot
/Subtype /Widget
diff --git a/testing/resources/javascript/named_action.in b/testing/resources/javascript/named_action.in
new file mode 100644
index 0000000..ba8d28b
--- /dev/null
+++ b/testing/resources/javascript/named_action.in
@@ -0,0 +1,33 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /OpenAction 10 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [
+ 3 0 R
+ ]
+>>
+endobj
+% Page number 0.
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 612 792]
+>>
+endobj
+% OpenAction action - not really JS, but generates text under test env.
+{{object 10 0}} <<
+ /Type /Action
+ /S /Named
+ /N (Print)
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/named_action_expected.txt b/testing/resources/javascript/named_action_expected.txt
new file mode 100644
index 0000000..1099a7f
--- /dev/null
+++ b/testing/resources/javascript/named_action_expected.txt
@@ -0,0 +1 @@
+Execute named action: Print
diff --git a/testing/resources/javascript/property_test_helpers.js b/testing/resources/javascript/property_test_helpers.js
index 47a25ef..0e405fc 100644
--- a/testing/resources/javascript/property_test_helpers.js
+++ b/testing/resources/javascript/property_test_helpers.js
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
diff --git a/testing/resources/javascript/public_methods_expected.txt b/testing/resources/javascript/public_methods_expected.txt
index 17abeff..70cd8ee 100644
--- a/testing/resources/javascript/public_methods_expected.txt
+++ b/testing/resources/javascript/public_methods_expected.txt
@@ -21,11 +21,11 @@
Alert: **********************
Alert: PASS: AFDate_KeystrokeEx() threw AFDate_KeystrokeEx: AFDate_KeystrokeEx's parameter size not correct
Alert: PASS: AFDate_KeystrokeEx(1, 2) threw AFDate_KeystrokeEx: AFDate_KeystrokeEx's parameter size not correct
-[icon=3,type=0]: The input value can't be parsed as a valid date/time (2).
+AFDate_KeystrokeEx[icon=3,type=0]: The input value can't be parsed as a valid date/time (2).
Alert: PASS: AFDate_KeystrokeEx(2) = x
-[icon=3,type=0]: The input value can't be parsed as a valid date/time (blooey).
+AFDate_KeystrokeEx[icon=3,type=0]: The input value can't be parsed as a valid date/time (blooey).
Alert: PASS: AFDate_KeystrokeEx('blooey') = x
-[icon=3,type=0]: The input value can't be parsed as a valid date/time (m/d).
+AFDate_KeystrokeEx[icon=3,type=0]: The input value can't be parsed as a valid date/time (m/d).
Alert: PASS: AFDate_KeystrokeEx('m/d') = x
Alert: **********************
Alert: PASS: AFExtractNums() threw AFExtractNums: Incorrect number of parameters passed to function.
@@ -50,7 +50,7 @@
Alert: **********************
Alert: PASS: AFNumber_Keystroke() threw AFNumber_Keystroke: Incorrect number of parameters passed to function.
Alert: PASS: AFNumber_Keystroke(1) threw AFNumber_Keystroke: Incorrect number of parameters passed to function.
-[icon=3,type=0]: The input value is invalid.
+AFNumber_Keystroke[icon=3,type=0]: The input value is invalid.
Alert: PASS: AFNumber_Keystroke(1, 2) threw AFNumber_Keystroke: The input value is invalid.
Alert: PASS: AFNumber_Keystroke(1, 2) = 123
Alert: PASS: AFNumber_Keystroke(1, 2, 3) = 123
@@ -277,19 +277,19 @@
Alert: **********************
Alert: PASS: AFPercent_Keystroke() threw AFPercent_Keystroke: Incorrect number of parameters passed to function.
Alert: PASS: AFPercent_Keystroke(1) threw AFPercent_Keystroke: Incorrect number of parameters passed to function.
-[icon=3,type=0]: The input value is invalid.
+AFNumber_Keystroke[icon=3,type=0]: The input value is invalid.
Alert: PASS: AFPercent_Keystroke(1, 0) threw AFPercent_Keystroke: The input value is invalid.
Alert: PASS: AFPercent_Keystroke(1, 0) = .123
Alert: **********************
Alert: PASS: AFRange_Validate() threw AFRange_Validate: Incorrect number of parameters passed to function.
Alert: PASS: AFRange_Validate(1, 2, 3, 4, 5) threw AFRange_Validate: Incorrect number of parameters passed to function.
-[icon=3,type=0]: The input value must be greater than or equal to 2 and less than or equal to 4.
+AFRange_Validate[icon=3,type=0]: The input value must be greater than or equal to 2 and less than or equal to 4.
Alert: PASS: AFRange_Validate(true, 2, true, 4) = 1
-[icon=3,type=0]: The input value must be greater than or equal to 2 and less than or equal to 4.
+AFRange_Validate[icon=3,type=0]: The input value must be greater than or equal to 2 and less than or equal to 4.
Alert: PASS: AFRange_Validate(true, 2, true, 4) = 5
-[icon=3,type=0]: The input value must be greater than or equal to 2.
+AFRange_Validate[icon=3,type=0]: The input value must be greater than or equal to 2.
Alert: PASS: AFRange_Validate(true, 2, false, 4) = 1
-[icon=3,type=0]: The input value must be less than or equal to 4.
+AFRange_Validate[icon=3,type=0]: The input value must be less than or equal to 4.
Alert: PASS: AFRange_Validate(false, 2, true, 4) = 5
Alert: PASS: AFRange_Validate(true, 2, true, 4) = 3
Alert: PASS: AFRange_Validate(false, 2, true, 4) = 1
@@ -345,11 +345,11 @@
Alert: **********************
Alert: PASS: AFSpecial_KeystrokeEx() threw AFSpecial_KeystrokeEx: Incorrect number of parameters passed to function.
Alert: PASS: AFSpecial_KeystrokeEx('') = 12345
-[icon=3,type=0]: The input value is invalid.
+AFSpecial_KeystrokeEx[icon=3,type=0]: The input value is invalid.
Alert: PASS: AFSpecial_KeystrokeEx('9999') = 123
-[icon=3,type=0]: The input value is too long.
+AFSpecial_KeystrokeEx[icon=3,type=0]: The input value is too long.
Alert: PASS: AFSpecial_KeystrokeEx('9999') = 12345
-[icon=3,type=0]: The input value is invalid.
+AFSpecial_KeystrokeEx[icon=3,type=0]: The input value is invalid.
Alert: PASS: AFSpecial_KeystrokeEx('9999') = abcd
Alert: PASS: AFSpecial_KeystrokeEx('9999') = 1234
Alert: PASS: AFSpecial_KeystrokeEx('XXXX') = abcd
@@ -385,13 +385,13 @@
Alert: PASS: AFSpecial_KeystrokeEx() threw AFSpecial_KeystrokeEx: Incorrect number of parameters passed to function.
Alert: PASS: AFSpecial_KeystrokeEx('') = 12345
Alert: PASS: AFSpecial_KeystrokeEx('9999') = 123
-[icon=3,type=0]: The input value is too long.
+AFSpecial_KeystrokeEx[icon=3,type=0]: The input value is too long.
Alert: PASS: AFSpecial_KeystrokeEx('9999') = 12345
-[icon=3,type=0]: The input value is too long.
+AFSpecial_KeystrokeEx[icon=3,type=0]: The input value is too long.
Alert: PASS: AFSpecial_KeystrokeEx('9999') = abcd
-[icon=3,type=0]: The input value is too long.
+AFSpecial_KeystrokeEx[icon=3,type=0]: The input value is too long.
Alert: PASS: AFSpecial_KeystrokeEx('9999') = 1234
-[icon=3,type=0]: The input value is too long.
+AFSpecial_KeystrokeEx[icon=3,type=0]: The input value is too long.
Alert: PASS: AFSpecial_KeystrokeEx('XXXX') = abcd
Alert: **********************
Alert: PASS: AFMergeChange() threw AFMergeChange: Incorrect number of parameters passed to function.
diff --git a/testing/resources/javascript/unsupported.in b/testing/resources/javascript/unsupported.in
index 4d1ad5e..eccfbab 100644
--- a/testing/resources/javascript/unsupported.in
+++ b/testing/resources/javascript/unsupported.in
@@ -19,6 +19,7 @@
/Parent 2 0 R
/MediaBox [0 0 612 792]
>>
+endobj
% OpenAction action
{{object 10 0}} <<
/Type /Action
@@ -26,7 +27,7 @@
/JS 11 0 R
>>
endobj
-% JS program to exexute
+% JS program to execute
{{object 11 0}} <<
{{streamlen}}
>>
diff --git a/testing/resources/javascript/util_bytetochar.in b/testing/resources/javascript/util_bytetochar.in
index 531e679..23dc423 100644
--- a/testing/resources/javascript/util_bytetochar.in
+++ b/testing/resources/javascript/util_bytetochar.in
@@ -23,6 +23,7 @@
/Contents [21 0 R]
/MediaBox [0 0 612 792]
>>
+endobj
% OpenAction action
{{object 10 0}} <<
/Type /Action
@@ -30,7 +31,7 @@
/JS 11 0 R
>>
endobj
-% JS program to exexute
+% JS program to execute
{{object 11 0}} <<
{{streamlen}}
>>
diff --git a/testing/resources/javascript/util_printd.in b/testing/resources/javascript/util_printd.in
index e4c8afc..d0d17b4 100644
--- a/testing/resources/javascript/util_printd.in
+++ b/testing/resources/javascript/util_printd.in
@@ -23,6 +23,7 @@
/Contents [21 0 R]
/MediaBox [0 0 612 792]
>>
+endobj
% OpenAction action
{{object 10 0}} <<
/Type /Action
@@ -30,7 +31,7 @@
/JS 11 0 R
>>
endobj
-% JS program to exexute
+% JS program to execute
{{object 11 0}} <<
{{streamlen}}
>>
diff --git a/testing/resources/javascript/util_printx.in b/testing/resources/javascript/util_printx.in
index 3002051..84d4846 100644
--- a/testing/resources/javascript/util_printx.in
+++ b/testing/resources/javascript/util_printx.in
@@ -23,6 +23,7 @@
/Contents [21 0 R]
/MediaBox [0 0 612 792]
>>
+endobj
% OpenAction action
{{object 10 0}} <<
/Type /Action
@@ -30,7 +31,7 @@
/JS 11 0 R
>>
endobj
-% JS program to exexute
+% JS program to execute
{{object 11 0}} <<
{{streamlen}}
>>
diff --git a/testing/resources/javascript/util_scand.in b/testing/resources/javascript/util_scand.in
index 25462d0..344aacd 100644
--- a/testing/resources/javascript/util_scand.in
+++ b/testing/resources/javascript/util_scand.in
@@ -27,7 +27,7 @@
/JS 11 0 R
>>
endobj
-% JS program to exexute
+% JS program to execute
{{object 11 0}} <<
{{streamlen}}
>>
diff --git a/testing/resources/javascript/v8_features.in b/testing/resources/javascript/v8_features.in
index 3d90473..8a5b6af 100644
--- a/testing/resources/javascript/v8_features.in
+++ b/testing/resources/javascript/v8_features.in
@@ -19,6 +19,7 @@
/Parent 2 0 R
/MediaBox [0 0 612 792]
>>
+endobj
% OpenAction action
{{object 10 0}} <<
/Type /Action
@@ -26,7 +27,7 @@
/JS 11 0 R
>>
endobj
-% JS program to exexute
+% JS program to execute
{{object 11 0}} <<
{{streamlen}}
>>
diff --git a/testing/resources/javascript/xfa_specific/bug_1004106.in b/testing/resources/javascript/xfa_specific/bug_1004106.in
new file mode 100644
index 0000000..926c39b
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1004106.in
@@ -0,0 +1,65 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+ {{streamlen}}
+>>
+stream
+<template>
+ <subform layout="tb" name="subform1">
+ <pageSet id="page" relation="orderedOccurrence">
+ <occur initial="1" max="1" min="1"/>
+ <pageArea id="Page1" name="Page1">
+ <occur max="1" min="1"/>
+ <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+ <medium long="297mm" short="210mm" stock="a4"/>
+ </pageArea>
+ </pageSet>
+ <subform layout="tb" name="subform2">
+ <variables name="vara">
+ </variables>
+ <occur initial="1" max="10" min="0" name="occur1">
+ </occur>
+ <field h="10mm" name="field1" w="40mm" x="10mm" y="10mm">
+ <event activity="ready" ref="$form">
+ <script contentType="application/x-javascript">
+ var ref = [];
+
+ app.test = function () {
+ var arr = []
+ var v1 = 1;
+ st = {};
+ var v2 = [0, st, 2, 3];
+ var v3 = [0, "poc", {}];
+ st.toString = function(){
+ for (var i = 0; i != 8; i++) v3.push({});
+ return "poc";
+ }
+
+ // Trigger
+ pfm_rt.Oneof(1, v1, v2, v3);
+ }
+ </script>
+ </event>
+ </field>
+ <field h="10mm" name="field2" w="40mm" x="10mm" y="10mm">
+ <event activity="ready" ref="$form">
+ <script contentType="application/x-formcalc">
+ app.test();
+ </script>
+ </event>
+ </field>
+ </subform>
+ </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/bug_1026991.evt b/testing/resources/javascript/xfa_specific/bug_1026991.evt
new file mode 100644
index 0000000..37d9022
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1026991.evt
@@ -0,0 +1,5 @@
+mousemove,0,0
+mousedown,left,0,0
+mouseup,left,0,0
+charcode,80
+mousemove,0,200
\ No newline at end of file
diff --git a/testing/resources/javascript/xfa_specific/bug_1026991.in b/testing/resources/javascript/xfa_specific/bug_1026991.in
new file mode 100644
index 0000000..4fb7657
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1026991.in
@@ -0,0 +1,65 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /AcroForm <<
+ /XFA 4 0 R
+ >>
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 612 792]
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+<xdp xmlns="http://ns.adobe.com/xdp/">
+ <config>
+ <acrobat>
+ <acrobat7>
+ <dynamicRender>required</dynamicRender>
+ </acrobat7>
+ </acrobat>
+ <present>
+ <pdf>
+ <interactive>1</interactive>
+ </pdf>
+ </present>
+ </config>
+ <template>
+ <subform>
+ <bookend leader="$"/>
+ <keep intact="none" previous="contentArea"/>
+ <field name="N01" minH="32in">
+ <ui>
+ <choiceList>
+ <margin rightInset="8in"/>
+ </choiceList>
+ </ui>
+ </field>
+ <field minH="32in">
+ <event activity="change">
+ <script>
+ $host.setFocus("N01")
+ </script>
+ </event>
+ </field>
+ </subform>
+ </template>
+</xdp>
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/bug_1039629.evt b/testing/resources/javascript/xfa_specific/bug_1039629.evt
new file mode 100644
index 0000000..96d37be
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1039629.evt
@@ -0,0 +1,3 @@
+mousedown,left,295,187
+mouseup,left,295,187
+keycode,40
\ No newline at end of file
diff --git a/testing/resources/javascript/xfa_specific/bug_1039629.in b/testing/resources/javascript/xfa_specific/bug_1039629.in
new file mode 100644
index 0000000..a911db6
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1039629.in
@@ -0,0 +1,188 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /AcroForm 3 0 R
+ /Pages 5 0 R
+ /NeedsRendering true
+>>
+endobj
+{{object 3 0}} <<
+ /XFA [
+ (preamble) 7 0 R
+ (config) 8 0 R
+ (template) 9 0 R
+ (form) 10 0 R
+ (postamble) 11 0 R
+ ]
+>>
+endobj
+{{object 5 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [ 12 0 R ]
+>>
+endobj
+{{object 7 0}} <<
+ {{streamlen}}
+>>
+stream
+<?xml version="1.0" encoding="UTF-8"?>
+<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/" timeStamp="2007-08-07T18:35:24Z" uuid="3b6c6ea3-3edf-40f5-90ff-51ee202e15b4">
+endstream
+endobj
+{{object 8 0}} <<
+ {{streamlen}}
+>>
+stream
+<config xmlns="http://www.xfa.org/schema/xci/2.6/">
+ <agent name="designer">
+ <destination>pdf</destination>
+ <pdf>
+ <!-- [0..n] -->
+ <fontInfo/>
+ </pdf>
+ </agent>
+ <present>
+ <!-- [0..n] -->
+ <pdf>
+ <!-- [0..n] -->
+ <version>1.7</version>
+ <renderPolicy>client</renderPolicy>
+ <creator>Adobe LiveCycle Designer ES 8.1</creator>
+ <producer>Adobe LiveCycle Designer ES 8.1</producer>
+ <scriptModel>XFA</scriptModel>
+ <interactive>1</interactive>
+ <tagged>1</tagged>
+ <fontInfo>
+ <embed>1</embed>
+ </fontInfo>
+ <compression>
+ <level>6</level>
+ <compressLogicalStructure>1</compressLogicalStructure>
+ </compression>
+ <linearized>1</linearized>
+ <viewerPreferences>
+ <addViewerPreferences>0</addViewerPreferences>
+ <duplexOption>simplex</duplexOption>
+ <numberOfCopies>0</numberOfCopies>
+ </viewerPreferences>
+ </pdf>
+ <cache>
+ <macroCache/>
+ <renderCache/>
+ </cache>
+ <incrementalMerge/>
+ <script>
+ <runScripts/>
+ <exclude/>
+ <currentPage/>
+ </script>
+ <copies/>
+ <layout/>
+ <xdp>
+ <packets>*</packets>
+ </xdp>
+ <destination>pdf</destination>
+ </present>
+ <psMap/>
+ <acrobat>
+ <acrobat7>
+ <dynamicRender>required</dynamicRender>
+ </acrobat7>
+ </acrobat>
+</config>
+endstream
+endobj
+{{object 9 0}} <<
+ {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/2.6/">
+ <subform name="form1">
+ <pageSet>
+ <pageArea id="Page1" name="Page1">
+ <contentArea h="10.5in" w="8in" x="0.25in" y="0.25in"/>
+ <medium long="11in" short="8.5in" stock="letter"/>
+ </pageArea>
+ </pageSet>
+ <subform h="10.5in" w="8in">
+ <field h="9.0001mm" name="ChoiceList" w="47.625mm" x="6.35mm" y="92.075mm">
+ <ui>
+ <choiceList/>
+ </ui>
+ <items>
+ <text>Foo</text>
+ </items>
+ </field>
+ <field h="100mm" name="ChoiceList1" w="100mm" x="0mm" y="0mm">
+ <ui>
+ <choiceList open="onEntry"/>
+ </ui>
+ <items>
+ <text>1111111111111</text>
+ <text>222222222222222</text>
+ </items>
+ <event activity="change">
+ <script contentType="application/x-javascript">
+ a += 1;
+ if (a == 2) {
+ list1 = xfa.resolveNode("ChoiceList");
+ xfa.host.setFocus(list1);
+ xfa.template.remerge();
+ xfa.host.openList(list1);
+ }
+ </script>
+ </event>
+ </field>
+ </subform>
+ <event activity="docReady">
+ <script contentType="application/x-javascript">
+ a = 0;
+ </script>
+ </event>
+ </subform>
+</template>
+endstream
+endobj
+{{object 10 0}} <<
+ {{streamlen}}
+>>
+stream
+<form xmlns="http://www.xfa.org/schema/xfa-form/2.6/">
+</form>
+endstream
+endobj
+{{object 11 0}} <<
+ {{streamlen}}
+>>
+stream
+</xdp:xdp>
+endstream
+endobj
+{{object 12 0}} <<
+ /Type /Page
+ /Contents 13 0 R
+ /CropBox [ 0.0 0.0 612.0 792.0 ]
+ /MediaBox [ 0.0 0.0 612.0 792.0 ]
+ /Parent 5 0 R
+ /Resources <<
+ /Font <<
+ /T1_0 14 0 R
+ >>
+ /ProcSet [ /PDF /Text ]
+ >>
+ /Rotate 0
+ /StructParents 0
+>>
+endobj
+{{object 14 0}} <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Helvetica
+ /Encoding /WinAnsiEncoding
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/bug_1040329.in b/testing/resources/javascript/xfa_specific/bug_1040329.in
new file mode 100644
index 0000000..7117084
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1040329.in
@@ -0,0 +1,99 @@
+{{header}}
+{{object 1 0}} <<
+ /Pages 2 0 R
+ /NeedsRendering true
+ /AcroForm <</XFA 5 0 R>>
+ /OpenAction 3 0 R
+ /Type /Catalog
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [ 4 0 R ]
+>>
+endobj
+{{object 3 0}} <<
+ /S /JavaScript
+ /JS (
+ )
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [ 0 0 795 842 ]
+>>
+endobj
+{{object 5 0}} <<
+ {{streamlen}}
+>>
+stream
+<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
+<template >
+ <subform allowMacro="0" layout="lr" mergeMode="consumeData" name="subform_0" >
+ <pageSet >
+ <occur initial="1" max="10" min="0" ></occur>
+ <pageArea >
+ <contentArea h="0.75in" w="0.25in" x="0.25in" y="0.25in" ></contentArea>
+ <subform allowMacro="0" layout="row" mergeMode="consumeData" name="subform_1" >
+ <event activity="initialize" >
+ <script contentType="application/x-javascript">
+ </script>
+ </event>
+ <field name="field_1" >
+ <bind match="dataRef" ref="a" ></bind>
+ <calculate >
+ <script contentType="application/x-javascript">
+ var f=xfa.resolveNode("xfa.form.subform_0");
+ xfa.host.setFocus(f);
+ f.mark ="square";
+ </script>
+ </calculate>
+ <event activity="docReady" >
+ <script contentType="application/x-javascript">
+ xfa.host.resetData(this);
+ </script>
+ </event>
+ </field>
+ </subform>
+ </pageArea>
+ </pageSet>
+ <field name="field_2" >
+ <bind match="dataRef" ref="a" ></bind>
+ <calculate >
+ <script contentType="application/x-javascript">
+ xfa.host.setFocus(this);
+ </script>
+ </calculate>
+ <items presence="hidden" save="0" >
+ <text >a</text>
+ <text >b</text>
+ <text >c</text>
+ </items>
+ <ui >
+ <choiceList commitOn="exit" open="onEntry" textEntry="1" ></choiceList>
+ </ui>
+ <value override="0" >
+ <text >a</text>
+ </value>
+ <event activity="initialize" >
+ <script contentType="application/x-javascript">
+ </script>
+ </event>
+ <event activity="change" >
+ <script contentType="application/x-javascript">
+ xfa.form.remerge();
+ xfa.host.openList(this);
+ </script>
+ </event>
+ </field>
+ </subform>
+</template>
+</xdp:xdp>
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/bug_1042915.evt b/testing/resources/javascript/xfa_specific/bug_1042915.evt
new file mode 100644
index 0000000..c0cea89
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1042915.evt
@@ -0,0 +1,8 @@
+mousedown,left,0,0
+mouseup,left,0,0
+keycode,9
+keycode,9
+mousedown,left,0,0
+mouseup,left,0,0
+mousedown,left,0,0
+mouseup,left,0,0
\ No newline at end of file
diff --git a/testing/resources/javascript/xfa_specific/bug_1042915.pdf b/testing/resources/javascript/xfa_specific/bug_1042915.pdf
new file mode 100644
index 0000000..36a2c8b
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1042915.pdf
Binary files differ
diff --git a/testing/resources/javascript/xfa_specific/bug_1042915_expected.txt b/testing/resources/javascript/xfa_specific/bug_1042915_expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1042915_expected.txt
diff --git a/testing/resources/javascript/xfa_specific/bug_1043508.in b/testing/resources/javascript/xfa_specific/bug_1043508.in
new file mode 100644
index 0000000..2afe129
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1043508.in
@@ -0,0 +1,62 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /AcroForm <<
+ /XFA 4 0 R
+ >>
+ /NeedsRendering true
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 3 3]
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+<xdp xmlns="http://ns.adobe.com/xdp/">
+<config>
+ <acrobat>
+ <acrobat7>
+ <dynamicRender>required</dynamicRender>
+ </acrobat7>
+ </acrobat>
+</config>
+<template>
+ <subform>
+ <breakBefore>
+ <script>
+ $.#field[1]=2
+ </script>
+ </breakBefore>
+ <field>
+ <ui>
+ <choiceList open="multiSelect"></choiceList>
+ </ui>
+ </field>
+ <field>
+ <event activity="change">
+ <script>
+ $host.openList("$form.#field[0]")
+ </script>
+ </event>
+ </field>
+ </subform>
+</template>
+</xdp>
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
\ No newline at end of file
diff --git a/testing/resources/javascript/xfa_specific/bug_1043510.evt b/testing/resources/javascript/xfa_specific/bug_1043510.evt
index 891a49d..08e98cf 100644
--- a/testing/resources/javascript/xfa_specific/bug_1043510.evt
+++ b/testing/resources/javascript/xfa_specific/bug_1043510.evt
@@ -1 +1 @@
-mousedoubleclick,left,0,0
\ No newline at end of file
+mousedoubleclick,left,0,0
diff --git a/testing/resources/javascript/xfa_specific/bug_1047914.evt b/testing/resources/javascript/xfa_specific/bug_1047914.evt
new file mode 100644
index 0000000..45b948d
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1047914.evt
@@ -0,0 +1,4 @@
+mousedown,left,0,2
+mouseup,left,0,2
+mousedown,left,0,2
+mouseup,left,0,2
\ No newline at end of file
diff --git a/testing/resources/javascript/xfa_specific/bug_1047914.in b/testing/resources/javascript/xfa_specific/bug_1047914.in
new file mode 100644
index 0000000..969d7a0
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1047914.in
@@ -0,0 +1,47 @@
+{{header}}
+{{object 1 0}} <<
+ /AcroForm <<
+ /XFA 2 0 R
+ >>
+>>
+endobj
+{{object 2 0}} <<
+ {{streamlen}}
+>>
+stream
+<xdp xmlns="http://ns.adobe.com/xdp/">
+ <config>
+ <acrobat>
+ <acrobat7>
+ <dynamicRender>required</dynamicRender>
+ </acrobat7>
+ </acrobat>
+ <present>
+ <pdf>
+ <interactive>1</interactive>
+ </pdf>
+ </present>
+ </config>
+ <template>
+ <subform>
+ <bookend leader="$"></bookend>
+ <keep previous="pageArea"></keep>
+ <field anchorType="middleCenter">
+ <ui>
+ <choiceList open="multiSelect">
+ <border><edge stroke="raised"></edge></border>
+ <margin></margin>
+ </choiceList>
+ </ui>
+ <items save="1"><float>41.0</float></items>
+ <items><float>43.0</float><integer>44</integer></items>
+ </field>
+ </subform>
+ </template>
+</xdp>
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
\ No newline at end of file
diff --git a/testing/resources/javascript/xfa_specific/bug_1052651.evt b/testing/resources/javascript/xfa_specific/bug_1052651.evt
new file mode 100644
index 0000000..e88ef06
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1052651.evt
@@ -0,0 +1,4 @@
+mousemove,100,100
+mousedown,left,100,100
+mouseup,left,100,100
+charcode,96
diff --git a/testing/resources/javascript/xfa_specific/bug_1052651.in b/testing/resources/javascript/xfa_specific/bug_1052651.in
new file mode 100644
index 0000000..a26312d
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1052651.in
@@ -0,0 +1,168 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /AcroForm 3 0 R
+ /Pages 5 0 R
+ /NeedsRendering true
+>>
+endobj
+{{object 3 0}} <<
+ /DA (/Helv 0 Tf 0 g )
+ /DR <<
+ /Font <<
+ /Helv 9 0 R
+ >>
+ >>
+ /XFA [
+ (preamble) 10 0 R
+ (config) 11 0 R
+ (template) 12 0 R
+ (form) 13 0 R
+ (postamble) 14 0 R
+ ]
+>>
+endobj
+{{object 5 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [6 0 R]
+>>
+endobj
+{{object 6 0}} <<
+ /Type /Page
+ /CropBox [0.0 0.0 612.0 792.0]
+ /MediaBox [0.0 0.0 612.0 792.0]
+ /Parent 5 0 R
+ /Resources <<
+ /ProcSet [/PDF /Text]
+ >>
+ /Rotate 0
+ /StructParents 0
+>>
+endobj
+{{object 8 0}} <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Helvetica
+ /Encoding /WinAnsiEncoding
+>>
+endobj
+{{object 9 0}} <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Helvetica
+ /Encoding /WinAnsiEncoding
+>>
+endobj
+{{object 10 0}} <<
+ {{streamlen}}
+>>
+stream
+<?xml version="1.0" encoding="UTF-8"?>
+<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
+endstream
+endobj
+{{object 11 0}} <<
+ {{streamlen}}
+>>
+stream
+<config xmlns="http://www.xfa.org/schema/xci/2.6/">
+ <agent name="designer">
+ <destination>pdf</destination>
+ <pdf>
+ <fontInfo/>
+ </pdf>
+ </agent>
+ <present>
+ <pdf>
+ <version>1.7</version>
+ <renderPolicy>client</renderPolicy>
+ <scriptModel>XFA</scriptModel>
+ <interactive>1</interactive>
+ </pdf>
+ <xdp>
+ <packets>*</packets>
+ </xdp>
+ <destination>pdf</destination>
+ </present>
+ <acrobat>
+ <acrobat7>
+ <dynamicRender>required</dynamicRender>
+ </acrobat7>
+ </acrobat>
+</config>
+endstream
+endobj
+{{object 12 0}} <<
+ {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/2.6/">
+ <subform layout="tb" locale="en_US" name="form1" restoreState="auto">
+ <pageSet>
+ <pageArea id="Page1" name="Page1">
+ <contentArea h="10.5in" w="8in" x="0.25in" y="0.25in"/>
+ <medium long="11in" short="8.5in" stock="letter"/>
+ </pageArea>
+ </pageSet>
+ <subform h="10.5in" w="8in" name="subform2">
+ <field h="9.0001mm" name="field1" w="47.625mm" x="6.35mm" y="92.075mm">
+ <ui>
+ <choiceList/>
+ </ui>
+ <items save="1">
+ <text>Single</text>
+ <text>Married</text>
+ <text>Other</text>
+ </items>
+ </field>
+ <field name="field2" h="200mm" w="200mm" x="1mm" y="1mm">
+ <ui>
+ <textEdit/>
+ </ui>
+ <value>
+ <text>pdfium</text>
+ </value>
+ <event activity="change">
+ <script contentType="application/x-javascript">
+ f2_change = f2_change + 1;
+ if (f2_change == 2) {
+ xfa.event.cancelAction = true;
+ f1 = xfa.resolveNode("xfa.form..field1");
+ xfa.host.setFocus(f1);
+ xfa.template.remerge();
+ xfa.host.openList(f1);
+ }
+ </script>
+ </event>
+ </field>
+ </subform>
+ <event activity="docReady">
+ <script contentType="application/x-javascript">
+ f2_change = 0;
+ f2 = xfa.resolveNode("xfa.form..field2");
+ xfa.host.setFocus(f2);
+ </script>
+ </event>
+ </subform>
+</template>
+endstream
+endobj
+{{object 13 0}} <<
+ {{streamlen}}
+>>
+stream
+<form xmlns="http://www.xfa.org/schema/xfa-form/2.6/"></form>
+endstream
+endobj
+{{object 14 0}} <<
+ {{streamlen}}
+>>
+stream
+</xdp:xdp>
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/bug_1052786.in b/testing/resources/javascript/xfa_specific/bug_1052786.in
new file mode 100644
index 0000000..b9dd5e9
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1052786.in
@@ -0,0 +1,168 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /AcroForm 3 0 R
+ /Pages 5 0 R
+ /NeedsRendering true
+>>
+endobj
+{{object 3 0}} <<
+ /DA (/Helv 0 Tf 0 g )
+ /DR <<
+ /Font <<
+ /Helv 9 0 R
+ >>
+ >>
+ /XFA [
+ (preamble) 10 0 R
+ (config) 11 0 R
+ (template) 12 0 R
+ (form) 13 0 R
+ (postamble) 14 0 R
+ ]
+>>
+endobj
+{{object 5 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [6 0 R]
+>>
+endobj
+{{object 6 0}} <<
+ /Type /Page
+ /CropBox [0.0 0.0 612.0 792.0]
+ /MediaBox [0.0 0.0 612.0 792.0]
+ /Parent 5 0 R
+ /Resources <<
+ /ProcSet [/PDF /Text]
+ >>
+ /Rotate 0
+>>
+endobj
+{{object 8 0}} <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Helvetica
+ /Encoding /WinAnsiEncoding
+>>
+endobj
+{{object 9 0}} <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Helvetica
+ /Encoding /WinAnsiEncoding
+>>
+endobj
+{{object 10 0}} <<
+ {{streamlen}}
+>>
+stream
+<?xml version="1.0" encoding="UTF-8"?>
+<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
+endstream
+endobj
+{{object 11 0}} <<
+ {{streamlen}}
+>>
+stream
+<config xmlns="http://www.xfa.org/schema/xci/2.6/">
+ <agent name="designer">
+ <destination>pdf</destination>
+ <pdf>
+ <fontInfo/>
+ </pdf>
+ </agent>
+ <present>
+ <pdf>
+ <version>1.7</version>
+ <renderPolicy>client</renderPolicy>
+ <scriptModel>XFA</scriptModel>
+ <interactive>1</interactive>
+ </pdf>
+ <xdp>
+ <packets>*</packets>
+ </xdp>
+ <destination>pdf</destination>
+ </present>
+ <psMap/>
+ <acrobat>
+ <acrobat7>
+ <dynamicRender>required</dynamicRender>
+ </acrobat7>
+ </acrobat>
+</config>
+endstream
+endobj
+{{object 12 0}} <<
+ {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/2.6/">
+ <subform layout="tb" locale="en_US" name="form1" restoreState="auto">
+ <pageSet>
+ <pageArea id="Page1" name="Page1">
+ <contentArea h="10.5in" w="8in" x="0.25in" y="0.25in"/>
+ <medium long="11in" short="8.5in" stock="letter"/>
+ </pageArea>
+ </pageSet>
+ <subform h="10.5in" w="8in" name="subform2">
+ <field h="9.0001mm" name="choiceList0" w="47.625mm" x="6.35mm" y="92.075mm">
+ <ui>
+ <choiceList/>
+ </ui>
+ <items save="1">
+ <text>Single</text>
+ <text>Married</text>
+ <text>Other</text>
+ </items>
+ </field>
+ <field name="choiceList1" h="10mm" w="1mm" x="5mm" y="50mm">
+ <ui>
+ <textEdit hScrollPolicy="off" vScrollPolicy="off" multiLine="0"/>
+ </ui>
+ <value>
+ <text maxChars="6">pdfium</text>
+ </value>
+ <event activity="full">
+ <script contentType="application/x-javascript">
+ full_count += 1;
+ if (full_count == 2) {
+ f1 = xfa.resolveNode("xfa.form..choiceList0");
+ xfa.host.setFocus(f1);
+ xfa.template.remerge();
+ xfa.host.openList(f1);
+ }
+ </script>
+ </event>
+ </field>
+ </subform>
+ <event activity="docReady">
+ <script contentType="application/x-javascript">
+ full_count = 0;
+ choiceList1 = xfa.resolveNode("xfa.form..choiceList1");
+ choiceList1.rawValue = "12345888888888888888";
+ xfa.host.setFocus(choiceList1);
+ </script>
+ </event>
+ </subform>
+</template>
+endstream
+endobj
+{{object 13 0}} <<
+ {{streamlen}}
+>>
+stream
+<form xmlns="http://www.xfa.org/schema/xfa-form/2.6/"></form>
+endstream
+endobj
+{{object 14 0}} <<
+ {{streamlen}}
+>>
+stream
+</xdp:xdp>
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/bug_1053617.in b/testing/resources/javascript/xfa_specific/bug_1053617.in
new file mode 100644
index 0000000..1e98bdc
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1053617.in
@@ -0,0 +1,155 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /AcroForm 2 0 R
+ /Pages 3 0 R
+ /Extensions <<
+ /ADBE <<
+ /BaseVersion /1.7
+ /ExtensionLevel 8
+ >>
+ >>
+ /NeedsRendering true
+>>
+endobj
+{{object 2 0}} <<
+ /XFA [
+ (preamble) 5 0 R
+ (config) 6 0 R
+ (template) 7 0 R
+ (postamble) 9 0 R
+ ]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [4 0 R]
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 612 792]
+>>
+endobj
+{{object 5 0}} <<
+ {{streamlen}}
+>>
+stream
+<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
+endstream
+endobj
+{{object 6 0}} <<
+ {{streamlen}}
+>>
+stream
+<config xmlns="http://www.xfa.org/schema/xci/3.0/">
+<agent name="designer">
+ <destination>pdf</destination>
+ <pdf>
+ <fontInfo/>
+ </pdf>
+</agent>
+<present>
+ <pdf>
+ <version>1.7</version>
+ <adobeExtensionLevel>8</adobeExtensionLevel>
+ <renderPolicy>client</renderPolicy>
+ <scriptModel>XFA</scriptModel>
+ <interactive>1</interactive>
+ </pdf>
+ <xdp>
+ <packets>*</packets>
+ </xdp>
+ <destination>pdf</destination>
+ <script>
+ <runScripts>server</runScripts>
+ </script>
+</present>
+<acrobat>
+ <acrobat7>
+ <dynamicRender>required</dynamicRender>
+ </acrobat7>
+ <validate>preSubmit</validate>
+</acrobat>
+</config>
+endstream
+endobj
+{{object 7 0}} <<
+ {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/2.6/">
+ <subform name="form1">
+ <pageSet>
+ <pageArea id="Page1" name="Page1">
+ <contentArea h="10.5in" w="8in" x="0.25in" y="0.25in"/>
+ <medium long="11in" short="8.5in" stock="letter"/>
+ </pageArea>
+ </pageSet>
+ <subform h="10.5in" w="8in" name="subform2">
+ <field name="f1" h="10mm" w="10mm" x="20mm" y="20mm">
+ <ui>
+ <choiceList/>
+ </ui>
+ <items save="1">
+ <text>Single</text>
+ </items>
+ </field>
+ <subform name="f4" x="5mm" y="5mm">
+ <occur max="-1"/>
+ <field name="f2" h="10mm" w="10mm" x="1mm" y="1mm">
+ <ui>
+ <dateTimeEdit/>
+ </ui>
+ <value>
+ <date/>
+ </value>
+ <format>
+ <picture>date{MM/DD/YY}</picture>
+ </format>
+ <event activity="initialize">
+ <script contentType="application/x-javascript">
+ this.rawValue = "2020" + "-" + "12" + "-" + "10";
+ </script>
+ </event>
+ <event activity="change">
+ <script contentType="application/x-javascript">
+ a = a + 1;
+ if (a == 2) {
+ xfa.event.cancelAction = true;
+ c = xfa.resolveNode("xfa.form..f1");
+ xfa.host.setFocus(c);
+ d = xfa.resolveNode("xfa.form..f4");
+ d.instanceManager.addInstance(1);
+ d.instanceManager.removeInstance(0);
+ xfa.host.openList(c);
+ }
+ </script>
+ </event>
+ </field>
+ </subform>
+ </subform>
+ <event activity="docReady">
+ <script contentType="application/x-javascript">
+ a = 0;
+ b = xfa.resolveNode("xfa.form..f2");
+ xfa.host.setFocus(b);
+ </script>
+ </event>
+ </subform>
+</template>
+endstream
+endobj
+{{object 9 0}} <<
+ {{streamlen}}
+>>
+stream
+</xdp:xdp>
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/bug_1054429.evt b/testing/resources/javascript/xfa_specific/bug_1054429.evt
new file mode 100644
index 0000000..86e0905
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1054429.evt
@@ -0,0 +1,2 @@
+mousedown,left,61,51
+keycode,46
\ No newline at end of file
diff --git a/testing/resources/javascript/xfa_specific/bug_1054429.in b/testing/resources/javascript/xfa_specific/bug_1054429.in
new file mode 100644
index 0000000..7295e5f
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1054429.in
@@ -0,0 +1,147 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /AcroForm 2 0 R
+ /Pages 3 0 R
+ /Extensions <<
+ /ADBE <<
+ /BaseVersion /1.7
+ /ExtensionLevel 8
+ >>
+ >>
+ /NeedsRendering true
+>>
+endobj
+{{object 2 0}} <<
+ /XFA [
+ (preamble) 5 0 R
+ (config) 6 0 R
+ (template) 7 0 R
+ (postamble) 9 0 R
+ ]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [4 0 R]
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 612 792]
+>>
+endobj
+{{object 5 0}} <<
+ {{streamlen}}
+>>
+stream
+<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
+endstream
+endobj
+{{object 6 0}} <<
+ {{streamlen}}
+>>
+stream
+<config xmlns="http://www.xfa.org/schema/xci/3.0/">
+<agent name="designer">
+ <destination>pdf</destination>
+ <pdf>
+ <fontInfo/>
+ </pdf>
+</agent>
+<present>
+ <pdf>
+ <version>1.7</version>
+ <adobeExtensionLevel>8</adobeExtensionLevel>
+ <renderPolicy>client</renderPolicy>
+ <scriptModel>XFA</scriptModel>
+ <interactive>1</interactive>
+ </pdf>
+ <xdp>
+ <packets>*</packets>
+ </xdp>
+ <destination>pdf</destination>
+ <script>
+ <runScripts>server</runScripts>
+ </script>
+</present>
+<acrobat>
+ <acrobat7>
+ <dynamicRender>required</dynamicRender>
+ </acrobat7>
+ <validate>preSubmit</validate>
+</acrobat>
+</config>
+endstream
+endobj
+{{object 7 0}} <<
+ {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/2.6/">
+ <subform layout="tb" locale="en_US" name="form1" restoreState="auto">
+ <pageSet>
+ <pageArea id="Page1" name="Page1">
+ <contentArea h="10.5in" w="8in" x="0.25in" y="0.25in"/>
+ <medium long="11in" short="8.5in" stock="letter"/>
+ </pageArea>
+ </pageSet>
+ <subform h="10.5in" w="8in" name="subform2">
+ <field h="10mm" name="f1" w="40mm" x="5mm" y="90mm">
+ <ui>
+ <choiceList/>
+ </ui>
+ <items save="1">
+ <text>Single</text>
+ </items>
+ </field>
+ <subform name="f4" x="5mm" y="5mm">
+ <occur max="-1"/>
+ <field name="f2" h="300mm" w="300mm" x="1mm" y="1mm">
+ <ui>
+ <textEdit/>
+ </ui>
+ <value>
+ <text>pdfiummmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm</text>
+ </value>
+ <event activity="change">
+ <script contentType="application/x-javascript">
+ a = a + 1;
+ if (a == 2) {
+ xfa.event.cancelAction = true;
+ b = xfa.resolveNode("xfa.form..f1");
+ xfa.host.setFocus(b);
+ d = xfa.resolveNode("xfa.form..f4");
+ d.instanceManager.addInstance(1);
+ d.instanceManager.removeInstance(0);
+ xfa.host.openList(b);
+ }
+ </script>
+ </event>
+ </field>
+ </subform>
+ </subform>
+ <event activity="docReady">
+ <script contentType="application/x-javascript">
+ a = 0;
+ c = xfa.resolveNode("xfa.form..f2");
+ xfa.host.setFocus(c);
+ </script>
+ </event>
+ </subform>
+</template>
+endstream
+endobj
+{{object 9 0}} <<
+ {{streamlen}}
+>>
+stream
+</xdp:xdp>
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/bug_1060549.evt b/testing/resources/javascript/xfa_specific/bug_1060549.evt
new file mode 100644
index 0000000..cee75e2
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1060549.evt
@@ -0,0 +1,3 @@
+mousedown,left,82,130
+mousedown,left,82,130
+keycode,09
\ No newline at end of file
diff --git a/testing/resources/javascript/xfa_specific/bug_1060549.in b/testing/resources/javascript/xfa_specific/bug_1060549.in
new file mode 100644
index 0000000..2652548
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1060549.in
@@ -0,0 +1,59 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+ {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/2.6/">
+ <subform layout="tb" locale="en_US" name="form1" restoreState="auto">
+ <subform h="10.5in" w="8in" name="subform2">
+ <field h="10mm" name="field1" w="10mm" x="0mm" y="20mm">
+ <ui>
+ <choiceList/>
+ </ui>
+ <items>
+ <text>aaaaaaaaaaaaaaaaaaaaa</text>
+ </items>
+ </field>
+ <field name="field2" h="20mm" w="50mm" x="0mm" y="30mm">
+ <ui>
+ <textEdit>
+ </textEdit>
+ </ui>
+ </field>
+ <subform name="subform3" x="0mm" y="5mm">
+ <occur max="-1"/>
+ <traversal>
+ <traverse operation="next" ref="$xfa.(eval('if (aa == 1) {aa=2;xfa.host.setFocus(cc);bb();xfa.host.openList(cc);}') == 0)"/>
+ </traversal>
+ </subform>
+ </subform>
+ <event activity="initialize">
+ <script contentType="application/x-javascript">
+ aa = 1;
+ </script>
+ </event>
+ <event activity="docReady">
+ <script contentType="application/x-javascript">
+ bb = function() {
+ dd = xfa.resolveNode("xfa.form..subform3");
+ dd.instanceManager.addInstance(1);
+ dd.instanceManager.removeInstance(0);
+ }
+ cc = xfa.resolveNode("xfa.form..field1");
+ </script>
+ </event>
+ </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/bug_1069700.evt b/testing/resources/javascript/xfa_specific/bug_1069700.evt
new file mode 100644
index 0000000..069f712
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1069700.evt
@@ -0,0 +1,3 @@
+mousedown,left,200,200
+mouseup,left,200,200
+keycode,9,shift
diff --git a/testing/resources/javascript/xfa_specific/bug_1069700.in b/testing/resources/javascript/xfa_specific/bug_1069700.in
new file mode 100644
index 0000000..1dd6ba8
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1069700.in
@@ -0,0 +1,73 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+ {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/2.6/">
+ <subform name="form1">
+ <pageSet>
+ <pageArea id="Page1" name="Page1">
+ <contentArea h="10.5in" w="8in" x="0.25in" y="0.25in"/>
+ <medium long="11in" short="8.5in" stock="letter"/>
+ </pageArea>
+ </pageSet>
+ <subform h="10.5in" w="8in" name="subform2">
+ <field h="10mm" name="DropDownList1" w="10mm" x="0mm" y="20mm">
+ <ui>
+ <choiceList/>
+ </ui>
+ <items save="1">
+ <text>Single</text>
+ <text>Married</text>
+ <text>Other</text>
+ </items>
+ </field>
+ <field h="200mm" name="DropDownList2" w="2mm" x="0mm" y="30mm">
+ <ui>
+ <textEdit/>
+ </ui>
+ </field>
+ <subform name="subform3" x="0mm" y="5mm">
+ <occur max="-1"/>
+ <traversal>
+ <traverse operation="next" ref="$xfa.(eval('try { if (aaaa == 1) {bb();} } catch(e){}') == 0)"/>
+ </traversal>
+ </subform>
+ <bookend leader="$"/>
+ <keep intact="none" previous="contentArea"/>
+ </subform>
+ <subform name="subform4" x="0mm">
+ <occur max="-1"/>
+ <field name="choiceList3" minH="32in">
+ <ui>
+ <choiceList/>
+ </ui>
+ </field>
+ </subform>
+ <event activity="docReady">
+ <script contentType="application/x-javascript">
+ aaaa = 1;
+ bb = function() {
+ xfa.host.setFocus(f1);
+ d = xfa.resolveNode("xfa.form..subform4");
+ d.instanceManager.addInstance(1);
+ xfa.host.openList(f1);
+ }
+ f1 = xfa.resolveNode("xfa.form..DropDownList1");
+ </script>
+ </event>
+ </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/bug_1069789.evt b/testing/resources/javascript/xfa_specific/bug_1069789.evt
new file mode 100644
index 0000000..768f6a9
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1069789.evt
@@ -0,0 +1 @@
+mousedown,right,200,200
\ No newline at end of file
diff --git a/testing/resources/javascript/xfa_specific/bug_1069789.in b/testing/resources/javascript/xfa_specific/bug_1069789.in
new file mode 100644
index 0000000..2cb09db
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1069789.in
@@ -0,0 +1,51 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+ {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/2.6/">
+ <subform name="form1">
+ <pageSet>
+ <pageArea id="Page1" name="Page1">
+ <contentArea h="10.5in" w="8in" x="0.25in" y="0.25in"/>
+ <medium long="11in" short="8.5in" stock="letter"/>
+ </pageArea>
+ </pageSet>
+ <subform h="10.5in" w="8in" name="subform2">
+ <field h="10mm" name="DropDownList1" w="10mm" x="0mm" y="20mm">
+ <ui>
+ <choiceList/>
+ </ui>
+ <items>
+ <text>Single</text>
+ </items>
+ </field>
+ <field h="200mm" name="DropDownList2" w="200mm" x="0mm" y="30mm">
+ <ui>
+ <textEdit/>
+ </ui>
+ <event activity="enter">
+ <script contentType="application/x-javascript">
+ f1 = xfa.resolveNode("xfa.form..DropDownList1");
+ xfa.host.setFocus(f1);
+ xfa.template.remerge();
+ xfa.host.openList(f1);
+ </script>
+ </event>
+ </field>
+ </subform>
+ </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/bug_1082597.in b/testing/resources/javascript/xfa_specific/bug_1082597.in
new file mode 100644
index 0000000..91a0ef0
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1082597.in
@@ -0,0 +1,150 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+ {{streanlen}}
+>>
+stream
+<template>
+ <subform layout="tb" name="my_doc">
+ <pageSet id="page" relation="orderedOccurrence">
+ <occur initial="1" max="4" min="1"/>
+ <pageArea id="Page1" name="Page1">
+ <occur max="1" min="1"/>
+ <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+ <medium long="297mm" short="210mm" stock="a4"/>
+ </pageArea>
+ <pageArea id="Page2" name="Page2">
+ <occur max="1" min="1"/>
+ <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+ <medium long="297mm" short="210mm" stock="a4"/>
+ </pageArea>
+ </pageSet>
+ <event activity="docReady" ref="$host">
+ <script contentType="application/x-javascript">
+ xfa.event.emit();
+ var f=xfa.resolveNode("xfa.form.my_doc.page1_form.combox");
+ xfa.record.nodes.append(this);
+ xfa.form.remerge();
+ xfa.host.setFocus(f);
+ app.alert('Done');
+ </script>
+ </event>
+ <subform layout="tb" name="subform_push_button_1">
+ <occur initial="1" max="10" min="0" name="occur_subform_push_button_1"/>
+ <field h="10mm" name="push_button" w="40mm" x="30mm" y="500mm">
+ <ui>
+ <button highlight="push"/>
+ </ui>
+ <caption>
+ <value>
+ <text>Button</text>
+ </value>
+ </caption>
+ <items>
+ <text name="down">Down Text</text>
+ <text name="rollover">Rollover Text</text>
+ </items>
+ </field>
+ </subform>
+ <subform layout="tb" name="subform_checkbutton_group_0">
+ <occur initial="1" max="10" min="0" name="occur_subform_checkbutton_group_0"/>
+ <exclGroup layout="tb" name="checkbutton_group">
+ <field h="10mm" name="checkbutton_check1" w="40mm" x="30mm" y="600mm">
+ <ui>
+ <checkButton shape="round">
+ <border>
+ <edge/>
+ </border>
+ </checkButton>
+ </ui>
+ <items>
+ <integer>1</integer>
+ </items>
+ <value>
+ <text>Select 1</text>
+ </value>
+ <caption placement="left">
+ <value>
+ <text>Option 1</text>
+ </value>
+ </caption>
+ <event activity="ready" ref="$layout">
+ <script contentType="application/x-javascript">
+ </script>
+ </event>
+ </field>
+ <field h="10mm" name="checkbutton_check2" w="40mm" x="30mm" y="600mm">
+ <ui>
+ <checkButton shape="round">
+ <border>
+ <edge/>
+ </border>
+ </checkButton>
+ </ui>
+ <items>
+ <integer>2</integer>
+ </items>
+ <value>
+ <text>Select 2</text>
+ </value>
+ <caption placement="left">
+ <value>
+ <text>Option 2</text>
+ </value>
+ </caption>
+ </field>
+ </exclGroup>
+ </subform>
+ <subform layout="tb" name="page2_form">
+ <occur initial="1" max="3" min="0"/>
+ <field h="10mm" name="textedit" w="40mm" x="20mm" y="20mm">
+ <ui>
+ <textEdit/>
+ </ui>
+ <value>
+ <text>CLGT.</text>
+ </value>
+ </field>
+ </subform>
+ <subform layout="tb" name="page1_form">
+ <occur initial="1" max="3" min="0"/>
+ <field h="10mm" name="combox" w="40mm" x="10mm" y="10mm">
+ <ui>
+ <choiceList open="onEntry">
+ <border>
+ <edge/>
+ </border>
+ </choiceList>
+ </ui>
+ <items save="1">
+ <text>apples</text>
+ <text>bananas</text>
+ <text>pears</text>
+ </items>
+ <value>
+ <text>
+ apples
+ </text>
+ </value>
+ <event activity="enter" name="event__enter">
+ <script contentType="application/x-javascript">
+ xfa.host.setFocus(0);
+ xfa.host.openList(this);
+ </script>
+ </event>
+ </field>
+ </subform>
+ </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/bug_1082597_expected.txt b/testing/resources/javascript/xfa_specific/bug_1082597_expected.txt
new file mode 100644
index 0000000..3cb772d
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1082597_expected.txt
@@ -0,0 +1,3 @@
+Alert: Done
+Alert: Done
+Alert: Done
diff --git a/testing/resources/javascript/xfa_specific/bug_1109108.evt b/testing/resources/javascript/xfa_specific/bug_1109108.evt
new file mode 100644
index 0000000..7d58927
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1109108.evt
@@ -0,0 +1 @@
+keycode,9
diff --git a/testing/resources/javascript/xfa_specific/bug_1109108.in b/testing/resources/javascript/xfa_specific/bug_1109108.in
new file mode 100644
index 0000000..550112e
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1109108.in
@@ -0,0 +1,64 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+ {{streamlen}}
+>>
+stream
+<template>
+ <subform name="s1">
+ <subform name="s5">
+ <field name="field_28">
+ <calculate>
+ <script contentType="application/x-javascript">
+ var f=xfa.resolveNode("xfa.form.s1.s5.s6.field_35");
+ f.relevant ="print";
+ </script>
+ </calculate>
+ <ui>
+ <textEdit/>
+ </ui>
+ <value>
+ <text>CLGT.</text>
+ </value>
+ </field>
+ <subform name="s6">
+ <field name="field_35">
+ <ui>
+ <choiceList open="onEntry">
+ <border>
+ <edge/>
+ </border>
+ </choiceList>
+ </ui>
+ <items save="1">
+ <text>apples</text>
+ <text>bananas</text>
+ <text>pears</text>
+ </items>
+ <value>
+ <text>apples</text>
+ </value>
+ <event activity="change">
+ <script contentType="application/x-javascript">
+ xfa.template.remerge();
+ xfa.host.openList(this);
+ app.alert('remerged');
+ </script>
+ </event>
+ </field>
+ </subform>
+ </subform>
+ </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/bug_1109108_expected.txt b/testing/resources/javascript/xfa_specific/bug_1109108_expected.txt
new file mode 100644
index 0000000..86c4845
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1109108_expected.txt
@@ -0,0 +1,2 @@
+Alert: remerged
+Alert: remerged
diff --git a/testing/resources/javascript/xfa_specific/bug_1137668.evt b/testing/resources/javascript/xfa_specific/bug_1137668.evt
new file mode 100644
index 0000000..d4b67d8
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1137668.evt
@@ -0,0 +1,9 @@
+keycode,9,shift
+mousedown,left,50,50
+keycode,9
+mousemove,50,50
+mousedown,left,50,50
+keycode,9
+mousemove,20,20
+mousedown,left,20,20
+mousedown,right,20,20
diff --git a/testing/resources/javascript/xfa_specific/bug_1137668.in b/testing/resources/javascript/xfa_specific/bug_1137668.in
new file mode 100644
index 0000000..11014e7
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1137668.in
@@ -0,0 +1,338 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+ {{streamlen}}
+>>
+stream
+<template>
+ <subform name="s1">
+ <subform name="s5">
+ <subform name="s6">
+ <event activity="enter">
+ <script contentType="application/x-javascript">
+ xfa.template.remerge();
+ xfa.form.remerge();
+ </script>
+ </event>
+ <field name="field_31">
+ <ui>
+ <button highlight="push"/>
+ </ui>
+ <caption>
+ <value>
+ <text>
+ Button
+ </text>
+ </value>
+ </caption>
+ <items>
+ <text name="down">
+ Down Text
+ </text>
+ <text name="rollover">
+ Rollover Text
+ </text>
+ </items>
+ </field>
+ <field name="field_32">
+ <ui>
+ <barcode/>
+ </ui>
+ <value>
+ <text>
+ test
+ </text>
+ </value>
+ </field>
+ <field name="field_33">
+ <ui>
+ <checkButton shape="round">
+ <border>
+ <edge/>
+ </border>
+ </checkButton>
+ </ui>
+ <items>
+ <integer>
+ 2
+ </integer>
+ </items>
+ <value>
+ <text>
+ Select 2
+ </text>
+ </value>
+ <caption placement="left">
+ <value>
+ <text>
+ Option 2
+ </text>
+ </value>
+ </caption>
+ </field>
+ <field name="field_34">
+ <ui>
+ <textEdit/>
+ </ui>
+ <value>
+ <text>
+ CLGT.
+ </text>
+ </value>
+ <event activity="ready">
+ <script contentType="application/x-javascript">
+ </script>
+ </event>
+ <event activity="enter">
+ <script contentType="application/x-javascript">
+ </script>
+ </event>
+ <event activity="initialize">
+ <script contentType="application/x-javascript">
+ </script>
+ </event>
+ </field>
+ <field name="field_35">
+ <validate>
+ <script contentType="application/x-javascript">
+ </script>
+ </validate>
+ <calculate>
+ <script contentType="application/x-javascript">
+ </script>
+ </calculate>
+ <ui>
+ <choiceList open="onEntry">
+ <border><edge/></border>
+ </choiceList>
+ </ui>
+ <items save="1">
+ <text>apples</text>
+ <text>bananas</text>
+ <text>pears</text>
+ </items>
+ <value>
+ <text>apples</text>
+ </value>
+ <event activity="docClose">
+ <script contentType="application/x-javascript">
+ </script>
+ </event>
+ <event activity="initialize">
+ <script contentType="application/x-javascript">
+ </script>
+ </event>
+ <event activity="change">
+ <script contentType="application/x-javascript">
+ </script>
+ </event>
+ </field>
+ <subform name="s7">
+ <field name="field_36">
+ <ui>
+ <dateTimeEdit>
+ <comb numberOfCells="8"/>
+ <border hand="right">
+ <edge/>
+ </border>
+ </dateTimeEdit>
+ </ui>
+ </field>
+ <field name="field_37">
+ <validate>
+ <script contentType="application/x-javascript">
+ </script>
+ </validate>
+ <calculate>
+ <script contentType="application/x-javascript">
+ </script>
+ </calculate>
+ <ui>
+ <button highlight="push"/>
+ </ui>
+ <caption>
+ <value>
+ <text>
+ Button
+ </text>
+ </value>
+ </caption>
+ <items>
+ <text name="down">
+ Down Text
+ </text>
+ <text name="rollover">
+ Rollover Text
+ </text>
+ </items>
+ <event activity="docClose">
+ <script contentType="application/x-javascript">
+ </script>
+ </event>
+ <event activity="change">
+ <script contentType="application/x-javascript">
+ </script>
+ </event>
+ <event activity="exit">
+ <script contentType="application/x-javascript">
+ </script>
+ </event>
+ </field>
+ <field name="field_38">
+ <validate>
+ <script contentType="application/x-javascript">
+ </script>
+ </validate>
+ <calculate>
+ <script contentType="application/x-javascript">
+ </script>
+ </calculate>
+ <ui>
+ <barcode/>
+ </ui>
+ <value>
+ <text>
+ test
+ </text>
+ </value>
+ <event activity="initialize">
+ <script contentType="application/x-javascript">
+ </script>
+ </event>
+ <event activity="change">
+ <script contentType="application/x-javascript">
+ </script>
+ </event>
+ <event activity="ready">
+ <script contentType="application/x-javascript">
+ </script>
+ </event>
+ </field>
+ <field name="field_39">
+ <validate>
+ <script contentType="application/x-javascript">
+ </script>
+ </validate>
+ <calculate>
+ <script contentType="application/x-javascript">
+ </script>
+ </calculate>
+ <ui>
+ <checkButton shape="round">
+ <border>
+ <edge/>
+ </border>
+ </checkButton>
+ </ui>
+ <items>
+ <integer>
+ 2
+ </integer>
+ </items>
+ <value>
+ <text>
+ Select 2
+ </text>
+ </value>
+ <caption placement="left">
+ <value>
+ <text>
+ Option 2
+ </text>
+ </value>
+ </caption>
+ <event activity="enter">
+ <script contentType="application/x-javascript">
+ </script>
+ </event>
+ <event activity="ready">
+ <script contentType="application/x-javascript">
+ </script>
+ </event>
+ <event activity="change">
+ <script contentType="application/x-javascript">
+ </script>
+ </event>
+ </field>
+ <field name="field_40">
+ <validate>
+ <script contentType="application/x-javascript">
+ </script>
+ </validate>
+ <calculate>
+ <script contentType="application/x-javascript">
+ </script>
+ </calculate>
+ <ui>
+ <textEdit/>
+ </ui>
+ <value>
+ <text>
+ CLGT.
+ </text>
+ </value>
+ <event activity="ready">
+ <script contentType="application/x-javascript">
+ </script>
+ </event>
+ <event activity="docClose">
+ <script contentType="application/x-javascript">
+ </script>
+ </event>
+ <event activity="exit">
+ <script contentType="application/x-javascript">
+ </script>
+ </event>
+ </field>
+ <field name="field_41">
+ <validate>
+ <script contentType="application/x-javascript">
+ </script>
+ </validate>
+ <calculate>
+ <script contentType="application/x-javascript">
+ </script>
+ </calculate>
+ <ui>
+ <choiceList open="onEntry">
+ <border><edge/></border>
+ </choiceList>
+ </ui>
+ <items save="1">
+ <text>apples</text>
+ <text>bananas</text>
+ <text>pears</text>
+ </items>
+ <value>
+ <text>apples</text>
+ </value>
+ <event activity="docClose">
+ <script contentType="application/x-javascript">
+ </script>
+ </event>
+ <event activity="initialize">
+ <script contentType="application/x-javascript">
+ </script>
+ </event>
+ <event activity="initialize">
+ <script contentType="application/x-javascript">
+ </script>
+ </event>
+ </field>
+ </subform>
+ </subform>
+ </subform>
+ </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/bug_1164158.in b/testing/resources/javascript/xfa_specific/bug_1164158.in
new file mode 100644
index 0000000..28b7eff
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1164158.in
@@ -0,0 +1,37 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+ {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/3.3/">
+ <subform>
+ <pageSet relation="simplexPaginated">
+ <pageArea pagePosition="last">
+ <subform>
+ <subform layout="table">
+ <subform layout="row">
+ <field />
+ <field colSpan="4294967295" presence="inactive" />
+ </subform>
+ </subform>
+ </subform>
+ </pageArea>
+ <pageArea>
+ <contentArea />
+ </pageArea>
+ </pageSet>
+ </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/bug_1248901.in b/testing/resources/javascript/xfa_specific/bug_1248901.in
new file mode 100644
index 0000000..8e2638a
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1248901.in
@@ -0,0 +1,47 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+ {{streamlen}}
+>>
+stream
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/2.6/">
+ <subform layout="tb" locale="en_US" name="form1" restoreState="auto">
+ <pageSet>
+ <pageArea id="Page1" name="Page1">
+ <contentArea h="10.5in" w="8in" x="0.25in" y="0.25in"/>
+ <medium long="11in" short="8.5in" stock="letter"/>
+ </pageArea>
+ </pageSet>
+ <subform h="10.5in" w="8in" name="subform2">
+ <field h="9.0001mm" name="field1" w="47.625mm" x="6.35mm" y="92.075mm">
+ <ui>
+ <choiceList/>
+ </ui>
+ </field>
+ </subform>
+ <event activity="docReady" ref="$host">
+ <script contentType="application/x-javascript">
+ try {
+ var form1 = xfa.resolveNode("xfa.form.form1");
+ var tmp = xfa.resolveNode("xfa.form..field1");
+ tmp.nodes.append(this);
+ } catch (e) {
+ app.alert("PASS: Caught: " + e);
+ }
+ </script>
+ </event>
+ </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/bug_1248901_expected.txt b/testing/resources/javascript/xfa_specific/bug_1248901_expected.txt
new file mode 100644
index 0000000..abf828c
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1248901_expected.txt
@@ -0,0 +1 @@
+Alert: PASS: Caught: XFAObject.append: Operation would create a cycle.
diff --git a/testing/resources/javascript/xfa_specific/bug_1312736.in b/testing/resources/javascript/xfa_specific/bug_1312736.in
new file mode 100644
index 0000000..f388810
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1312736.in
@@ -0,0 +1,40 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{object 3 0}} <<
+ {{streamlen}}
+>>
+stream
+<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
+<template x="">
+ <subform>
+ <pageSet>
+ <pageArea>
+ <contentArea/>
+ <exclGroup name="0">
+ <field>
+ <ui><checkButton/></ui>
+ <items><textEdit/></items>
+ </field>
+ </exclGroup>
+ <subform name="Sho0">
+ <event activity="initialize">
+ <script contentType="application/x-javascript">
+ Sho0.presence=0;
+ app.alert("done");
+ </script>
+ </event>
+ </subform>
+ </pageArea>
+ </pageSet>
+ </subform>
+</template>
+endstream
+endobj
+{{object 8 0} <<
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/bug_1312736_expected.txt b/testing/resources/javascript/xfa_specific/bug_1312736_expected.txt
new file mode 100644
index 0000000..daa1eca
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1312736_expected.txt
@@ -0,0 +1 @@
+Alert: done
diff --git a/testing/resources/javascript/xfa_specific/bug_1444238.evt b/testing/resources/javascript/xfa_specific/bug_1444238.evt
new file mode 100644
index 0000000..adca35a
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1444238.evt
@@ -0,0 +1,3 @@
+mousedown,left,91,539
+mouseup,left,91,539
+charcode,32
diff --git a/testing/resources/javascript/xfa_specific/bug_1444238.in b/testing/resources/javascript/xfa_specific/bug_1444238.in
new file mode 100644
index 0000000..675178c
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_1444238.in
@@ -0,0 +1,149 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /AcroForm 4 0 R
+ /OpenAction 40 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 2
+ /Kids [
+ 32 0 R
+ 34 0 R
+ ]
+>>
+endobj
+% Forms
+{{object 4 0}} <<
+ /XFA 43 0 R
+ /Fields [
+ 10 0 R
+ 11 0 R
+ ]
+>>
+endobj
+% Fields
+{{object 10 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Tx
+ /T (MyField5)
+ /V (myfield_5)
+ /Rect [0 500 600 600]
+>>
+% Fields
+{{object 11 0}} <<
+ /T (MyField3)
+ /Parent 4 0 R
+ /Kids [12 0 R]
+ /Opt [(a) (b) (c) (d)]
+ /V [(a) (b) (c)]
+>>
+endobj
+% Fields
+{{object 12 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Ch
+ /Ff 131072
+ /Parent 11 0 R
+ /Kids [13 0 R]
+>>
+endobj
+% Fields
+{{object 13 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /Parent 12 0 R
+ /Rect [0 400 600 600]
+>>
+endobj
+% Fields
+{{object 14 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /Parent 12 0 R
+ /Rect [100 400 500 500]
+>>
+endobj
+% Page number 2.
+{{object 32 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 612 792]
+ /Annots [13 0 R]
+
+>>
+endobj
+{{object 34 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 612 792]
+ /Annots [10 0 R]
+>>
+endobj
+% Document JS Action
+{{object 40 0}} <<
+ /Type /Action
+ /S /JavaScript
+ /JS 41 0 R
+>>
+endobj
+% JS program to exexute
+{{object 41 0}} <<
+>>
+stream
+var f5 = this.getField("MyField5");
+var f3 = this.getField("MyField3");
+f3.setFocus();
+this.__defineGetter__("pageNum",function o(){f5.setFocus(); f3.borderStyle="dashed"; f3.setFocus();});
+endstream
+endobj
+{{object 43 0}} <<
+ {{streamlen}}
+>>
+stream
+<?xml version="1.0" encoding="UTF-8"?>
+<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">
+<config></config>
+<template xmlns="http://www.xfa.org/schema/xfa-template/2.8/">
+ <subform layout="tb" locale="en_US">
+ <pageSet>
+ <pageArea id="Page1" name="Page1">
+ <contentArea h="268.939mm" w="203.2mm" x="6.35mm" y="6.35mm"/>
+ <medium long="792pt" short="612pt" stock="default"/>
+ </pageArea>
+ </pageSet>
+ <field h="9.0001mm" name="MyField3" w="47.625mm" x="120mm" y="120mm">
+ <ui>
+ <choiceList open="onEntry">
+ <border>
+ <edge/>
+ </border>
+ </choiceList>
+ </ui>
+ <items save="1">
+ <text>apples</text>
+ <text>bananas</text>
+ <text>pears</text>
+ </items>
+ <value>
+ <text>apples</text>
+ </value>
+ <event activity="preOpen">
+ <script contentType="application/x-javascript">
+ var aa = this.pageNum;
+ </script>
+ </event>
+ </field>
+ </subform>
+</template>
+</xdp:xdp>
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/bug_980116.evt b/testing/resources/javascript/xfa_specific/bug_980116.evt
new file mode 100644
index 0000000..a5025fa
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_980116.evt
@@ -0,0 +1,4 @@
+mousemove,50,50
+mousedown,left,50,50
+mouseup,left,50,50
+keycode,9
diff --git a/testing/resources/javascript/xfa_specific/bug_980116.in b/testing/resources/javascript/xfa_specific/bug_980116.in
new file mode 100644
index 0000000..11bcbf2
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_980116.in
@@ -0,0 +1,58 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+ {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/2.6/">
+ <subform layout="tb" locale="en_US" name="form1" restoreState="auto">
+ <pageSet>
+ <pageArea id="Page1" name="Page1">
+ <contentArea h="10.5in" w="8in" x="0.25in" y="0.25in"/>
+ <medium long="11in" short="8.5in" stock="letter"/>
+ </pageArea>
+ </pageSet>
+ <subform h="10.5in" w="8in">
+ <field h="3mm" name="DropDownList1" w="3mm" x="0mm" y="1mm">
+ <ui>
+ <choiceList/>
+ </ui>
+ <items save="1">
+ <text>Single</text>
+ <text>Married</text>
+ <text>Other</text>
+ </items>
+ </field>
+ <field h="500.0001mm" name="DropDownList2" w="500.625mm" x="0mm" y="0mm">
+ <ui>
+ <textEdit></textEdit>
+ </ui>
+ <value>
+ <text>Employee</text>
+ </value>
+ </field>
+ </subform>
+ <traversal>
+ <traverse operation="first" ref="$xfa.(eval('xfa.host.setFocus(field_DropDownList1); xfa.template.remerge(); xfa.host.openList(field_DropDownList1);') == 0)"/>
+ </traversal>
+ <event activity="docReady">
+ <script contentType="application/x-javascript">
+ field_DropDownList1 = xfa.resolveNode("xfa.form..DropDownList1");
+ field_DropDownList2 = xfa.resolveNode("xfa.form..DropDownList2");
+ xfa.host.setFocus(field_DropDownList2);
+ </script>
+ </event>
+ </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/bug_980161.evt b/testing/resources/javascript/xfa_specific/bug_980161.evt
new file mode 100644
index 0000000..a5025fa
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_980161.evt
@@ -0,0 +1,4 @@
+mousemove,50,50
+mousedown,left,50,50
+mouseup,left,50,50
+keycode,9
diff --git a/testing/resources/javascript/xfa_specific/bug_980161.in b/testing/resources/javascript/xfa_specific/bug_980161.in
new file mode 100644
index 0000000..058444d
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/bug_980161.in
@@ -0,0 +1,55 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+ {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/2.6/">
+ <subform layout="tb" locale="en_US" name="form1" restoreState="auto">
+ <pageSet>
+ <pageArea id="Page1" name="Page1">
+ <contentArea h="10.5in" w="8in" x="0.25in" y="0.25in"/>
+ <medium long="11in" short="8.5in" stock="letter"/>
+ </pageArea>
+ </pageSet>
+ <subform h="10.5in" w="8in">
+ <field h="3mm" name="DropDownList1" w="3mm" x="0mm" y="1mm">
+ <ui>
+ <choiceList/>
+ </ui>
+ <items save="1">
+ <text>Single</text>
+ <text>Married</text>
+ <text>Other</text>
+ </items>
+ </field>
+ <field h="500.0001mm" name="DropDownList2" w="500.625mm" x="0mm" y="0mm">
+ <ui>
+ <textEdit></textEdit>
+ </ui>
+ <traversal>
+ <traverse operation="next" ref="$xfa.(eval('xfa.host.setFocus(field_DropDownList1); xfa.template.remerge(); xfa.host.openList(field_DropDownList1);') == 0)"/>
+ </traversal>
+ </field>
+ </subform>
+ <event activity="docReady">
+ <script contentType="application/x-javascript">
+ field_DropDownList1 = xfa.resolveNode("xfa.form..DropDownList1");
+ field_DropDownList2 = xfa.resolveNode("xfa.form..DropDownList2");
+ xfa.host.setFocus(field_DropDownList2);
+ </script>
+ </event>
+ </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/cross_engine_apply.in b/testing/resources/javascript/xfa_specific/cross_engine_apply.in
index cdaad2a..3a41708 100644
--- a/testing/resources/javascript/xfa_specific/cross_engine_apply.in
+++ b/testing/resources/javascript/xfa_specific/cross_engine_apply.in
@@ -46,6 +46,7 @@
endobj
{{include ../../xfa_locale_6_0.fragment}}
{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
{{xref}}
{{trailer}}
{{startxref}}
diff --git a/testing/resources/javascript/xfa_specific/dump_tree.js b/testing/resources/javascript/xfa_specific/dump_tree.js
new file mode 100644
index 0000000..171c72a
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/dump_tree.js
@@ -0,0 +1,22 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Note: Be sure that this file is only included inside a CDATA block,
+// otherwise the less-than comparision below will break the XML parse.
+
+function dumpTree(node, level) {
+ level = level || 0;
+ var indentation = "| ".repeat(level);
+ try {
+ app.alert(indentation + node.className);
+ var children = node.nodes;
+ if (children) {
+ for (var i = 0; i < children.length; ++i) {
+ dumpTree(children.item(i), level + 1);
+ }
+ }
+ } catch (e) {
+ app.alert(indentation + "Error: " + e);
+ }
+}
diff --git a/testing/resources/javascript/xfa_specific/instance_manager.in b/testing/resources/javascript/xfa_specific/instance_manager.in
new file mode 100644
index 0000000..fe3d776
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/instance_manager.in
@@ -0,0 +1,99 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+ {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/2.6/">
+ <subform layout="tb" locale="en_US" name="form1" restoreState="auto">
+ <pageSet>
+ <pageArea id="Page1" name="Page1">
+ <contentArea h="10.5in" w="8in" x="0.25in" y="0.25in"/>
+ <medium long="11in" short="8.5in" stock="letter"/>
+ </pageArea>
+ </pageSet>
+ <subform h="10.5in" w="8in" name="subform2">
+ <field h="9.0001mm" name="field1" w="47.625mm" x="6.35mm" y="92.075mm">
+ <ui>
+ <choiceList/>
+ </ui>
+ <items save="1">
+ <text>Single</text>
+ <text>Married</text>
+ <text>Other</text>
+ </items>
+ </field>
+ <field name="field3" h="10.625mm" w="30.625mm" x="5mm" y="50mm">
+ </field>
+ <subform name="field4" x="5mm" y="5mm">
+ <occur max="-1"/>
+ <field name="field5" w="64.77mm" h="6.35mm">
+ </field>
+ </subform>
+ </subform>
+ <event activity="docReady">
+ <script contentType="application/x-javascript"><![CDATA[
+ {{include ../expect.js}}
+ {{include ../property_test_helpers.js}}
+ {{include dump_tree.js}}
+
+ var mgr = xfa.resolveNode("xfa.form..field4").instanceManager;
+ dumpTree(mgr);
+
+ testRWProperty(mgr, "count", 1, 12);
+ testROProperty(mgr, "min", 1);
+ testROProperty(mgr, "max", -1);
+
+ expectError("mgr.setInstances()");
+ expectError("mgr.setInstances(-10)");
+ expectError("mgr.setInstances('clams')");
+ expectError("mgr.setInstances([1, 2, 3])");
+ // setInstances(10000000) will hang or hit OOM.
+ expect("mgr.setInstances(4)", undefined);
+ expect("mgr.count", 4);
+ expect("mgr.setInstances(2)", undefined);
+ expect("mgr.count", 2);
+
+ expectError("mgr.moveInstance()");
+ expectError("mgr.moveInstance(0)");
+ expectError("mgr.moveInstance('clams')");
+ expectError("mgr.moveInstance([1, 2, 3])");
+ expect("mgr.moveInstance(0, 1)", undefined);
+ expect("mgr.count", 2);
+
+ expectError("mgr.addInstance(1, 2, 3)");
+ expect("mgr.addInstance().className", "subform");
+ expect("mgr.addInstance(true).className", "subform");
+ expect("mgr.count", 4);
+
+ expectError("mgr.insertInstance()");
+ expectError("mgr.insertInstance(1, 2, 3)");
+ expect("mgr.insertInstance(1, true).className", "subform");
+ expect("mgr.count", 5);
+
+ expectError("mgr.removeInstance()");
+ expectError("mgr.removeInstance(1, 2)");
+ expect("mgr.removeInstance(0)", undefined);
+ expect("mgr.removeInstance(0)", undefined);
+ expect("mgr.removeInstance(0)", undefined);
+ expect("mgr.removeInstance(0)", undefined);
+ expect("mgr.count", 1);
+
+ expectError("mgr.removeInstance(0)");
+ expect("mgr.count", 1);
+ ]]></script>
+ </event>
+ </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/instance_manager_expected.txt b/testing/resources/javascript/xfa_specific/instance_manager_expected.txt
new file mode 100644
index 0000000..d39657f
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/instance_manager_expected.txt
@@ -0,0 +1,39 @@
+Alert: instanceManager
+Alert: | occur
+Alert: PASS: count = 1
+Alert: PASS: count = 12
+Alert: PASS: min = 1
+Alert: PASS: min threw Error: Invalid property set operation.
+Alert: PASS: max = -1
+Alert: PASS: max threw Error: Invalid property set operation.
+Alert: PASS: mgr.setInstances() threw XFAObject.setInstances: Incorrect number of parameters passed to function.
+Alert: PASS: mgr.setInstances(-10) threw Error: The element [min] has violated its allowable number of occurrences.
+Alert: PASS: mgr.setInstances('clams') threw Error: The element [min] has violated its allowable number of occurrences.
+Alert: PASS: mgr.setInstances([1, 2, 3]) threw Error: The element [min] has violated its allowable number of occurrences.
+Alert: PASS: mgr.setInstances(4) = undefined
+Alert: PASS: mgr.count = 4
+Alert: PASS: mgr.setInstances(2) = undefined
+Alert: PASS: mgr.count = 2
+Alert: PASS: mgr.moveInstance() threw XFAObject.moveInstance: Incorrect number of parameters passed to function.
+Alert: PASS: mgr.moveInstance(0) threw XFAObject.moveInstance: Incorrect number of parameters passed to function.
+Alert: PASS: mgr.moveInstance('clams') threw XFAObject.moveInstance: Incorrect number of parameters passed to function.
+Alert: PASS: mgr.moveInstance([1, 2, 3]) threw XFAObject.moveInstance: Incorrect number of parameters passed to function.
+Alert: PASS: mgr.moveInstance(0, 1) = undefined
+Alert: PASS: mgr.count = 2
+Alert: PASS: mgr.addInstance(1, 2, 3) threw XFAObject.addInstance: Incorrect number of parameters passed to function.
+Alert: PASS: mgr.addInstance().className = subform
+Alert: PASS: mgr.addInstance(true).className = subform
+Alert: PASS: mgr.count = 4
+Alert: PASS: mgr.insertInstance() threw XFAObject.insertInstance: Incorrect number of parameters passed to function.
+Alert: PASS: mgr.insertInstance(1, 2, 3) threw XFAObject.insertInstance: Incorrect number of parameters passed to function.
+Alert: PASS: mgr.insertInstance(1, true).className = subform
+Alert: PASS: mgr.count = 5
+Alert: PASS: mgr.removeInstance() threw XFAObject.removeInstance: Incorrect number of parameters passed to function.
+Alert: PASS: mgr.removeInstance(1, 2) threw XFAObject.removeInstance: Incorrect number of parameters passed to function.
+Alert: PASS: mgr.removeInstance(0) = undefined
+Alert: PASS: mgr.removeInstance(0) = undefined
+Alert: PASS: mgr.removeInstance(0) = undefined
+Alert: PASS: mgr.removeInstance(0) = undefined
+Alert: PASS: mgr.count = 1
+Alert: PASS: mgr.removeInstance(0) threw XFAObject.removeInstance: Too many occurrences.
+Alert: PASS: mgr.count = 1
diff --git a/testing/resources/javascript/xfa_specific/mixed_widgets.evt b/testing/resources/javascript/xfa_specific/mixed_widgets.evt
new file mode 100644
index 0000000..f4ee07d
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/mixed_widgets.evt
@@ -0,0 +1,5 @@
+mousemove,302,302
+mousedown,left,302,302
+mouseup,left,302,302
+charcode,65
+charcode,66
diff --git a/testing/resources/javascript/xfa_specific/mixed_widgets.in b/testing/resources/javascript/xfa_specific/mixed_widgets.in
new file mode 100644
index 0000000..2974112
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/mixed_widgets.in
@@ -0,0 +1,48 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /NeedsRendering true
+ /AcroForm <<
+ /Fields [6 0 R]
+ /XFA 5 0 R
+ >>
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [4 0 R]
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 512 512]
+ /Annots [6 0 R]
+>>
+endobj
+{{object 5 0}} <<
+ {{streamlen}}
+>>
+stream
+<xdp xmlns="http://ns.adobe.com/xdp/">
+ <template>
+ <subform x="0pt" y="0pt" w="198pt" h="198pt">
+ <field name="MyBox"/>
+ </subform>
+ </template>
+</xdp>
+endstream
+endobj
+{{object 6 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Tx
+ /Rect [200 200 512 512]
+ /T (MyBox)
+>>
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/popup_menu.evt b/testing/resources/javascript/xfa_specific/popup_menu.evt
new file mode 100644
index 0000000..7820426
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/popup_menu.evt
@@ -0,0 +1,5 @@
+mousemove,20,20
+mousedown,left,20,20
+mouseup,left,20,20
+mousedown,right,20,20
+mouseup,right,20,20
diff --git a/testing/resources/javascript/xfa_specific/popup_menu.in b/testing/resources/javascript/xfa_specific/popup_menu.in
new file mode 100644
index 0000000..52f7a45
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/popup_menu.in
@@ -0,0 +1,40 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+ {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/3.3/">
+ <subform name="form1" layout="tb" locale="en_US" restoreState="auto">
+ <pageSet>
+ <pageArea name="Page1" id="Page1">
+ <contentArea x="18pt" y="18pt" w="612pt" h="792pt"/>
+ <medium stock="default" short="612pt" long="792pt"/>
+ </pageArea>
+ </pageSet>
+ <subform w="576pt" h="756pt" name="Page1">
+ <field name="ImageField1" y="0pt" x="0pt" w="425pt" h="80pt">
+ <ui>
+ <choiceList open="userControl"/>
+ </ui>
+ <items save="1">
+ <text>clams</text>
+ <text>oysters</text>
+ <text>crabs</text>
+ </items>
+ </field>
+ </subform>
+ </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/popup_menu_expected.txt b/testing/resources/javascript/xfa_specific/popup_menu_expected.txt
new file mode 100644
index 0000000..dc856e9
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/popup_menu_expected.txt
@@ -0,0 +1 @@
+Popup: x=20.0, y=20.0, flags=0x0
diff --git a/testing/resources/javascript/xfa_specific/xfa_exclgroup.in b/testing/resources/javascript/xfa_specific/xfa_exclgroup.in
new file mode 100644
index 0000000..1a29352
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_exclgroup.in
@@ -0,0 +1,86 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+ {{streamlen}}
+>>
+stream
+<template>
+ <subform layout="tb">
+ <exclGroup name="RadioButtonList" layout="lr-tb">
+ <field w="30mm" h="6mm" name="yes">
+ <ui>
+ <checkButton shape="round">
+ </checkButton>
+ </ui>
+ <items>
+ <text>Agree</text>
+ </items>
+ </field>
+ <field w="30mm" h="6mm" name="no">
+ <ui>
+ <checkButton shape="round">
+ </checkButton>
+ </ui>
+ <items>
+ <text>Disagree</text>
+ </items>
+ </field>
+ <validate nullTest="error"/>
+ </exclGroup>
+ <event activity="initialize">
+ <script contentType="application/x-javascript"><![CDATA[
+ {{include ../expect.js}}
+ expect("RadioButtonList.className", "exclGroup");
+
+ expectError("RadioButtonList.execEvent()");
+ expectError("RadioButtonList.execEvent(1, 2)");
+ expect("RadioButtonList.execEvent('nonesuch')", null);
+ expect("RadioButtonList.execEvent('calculate')", null);
+ expect("RadioButtonList.execEvent('change')", null);
+ expect("RadioButtonList.execEvent('click')", null);
+ expect("RadioButtonList.execEvent('enter')", null);
+ expect("RadioButtonList.execEvent('exit')", null);
+ expect("RadioButtonList.execEvent('full')", null);
+ expect("RadioButtonList.execEvent('indexChange')", null);
+ expect("RadioButtonList.execEvent('initialize')", null);
+ expect("RadioButtonList.execEvent('mouseDown')", null);
+ expect("RadioButtonList.execEvent('mouseEnter')", null);
+ expect("RadioButtonList.execEvent('mouseExit')", null);
+ expect("RadioButtonList.execEvent('mouseUp')", null);
+ expect("RadioButtonList.execEvent('postOpen')", null);
+ expect("RadioButtonList.execEvent('preOpen')", null);
+ expect("RadioButtonList.execEvent('preSign')", null);
+ expect("RadioButtonList.execEvent('validate')", null);
+
+ expectError("RadioButtonList.execInitialize('badarg')");
+ expect("RadioButtonList.execInitialize()", null);
+
+ expectError("RadioButtonList.execCalculate('badarg')");
+ expect("RadioButtonList.execCalculate()", null);
+
+ expectError("RadioButtonList.execValidate('badarg')");
+ expect("RadioButtonList.execValidate()", true);
+
+ expectError("RadioButtonList.selectedMember('badarg')");
+ expect("RadioButtonList.selectedMember()", null);
+
+ expect("RadioButtonList.defaultValue", undefined);
+ expect("RadioButtonList.rawValue", null);
+ expect("RadioButtonList.transient", undefined);
+ expect("RadioButtonList.errorText", undefined);
+ ]]></script>
+ </event>
+ </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/xfa_exclgroup_expected.txt b/testing/resources/javascript/xfa_specific/xfa_exclgroup_expected.txt
new file mode 100644
index 0000000..7ca5a78
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_exclgroup_expected.txt
@@ -0,0 +1,32 @@
+Alert: PASS: RadioButtonList.className = exclGroup
+Alert: PASS: RadioButtonList.execEvent() threw XFAObject.execEvent: Incorrect number of parameters passed to function.
+Alert: PASS: RadioButtonList.execEvent(1, 2) threw XFAObject.execEvent: Incorrect number of parameters passed to function.
+Alert: PASS: RadioButtonList.execEvent('nonesuch') = undefined
+Alert: PASS: RadioButtonList.execEvent('calculate') = undefined
+Alert: PASS: RadioButtonList.execEvent('change') = undefined
+Alert: PASS: RadioButtonList.execEvent('click') = undefined
+Alert: PASS: RadioButtonList.execEvent('enter') = undefined
+Alert: PASS: RadioButtonList.execEvent('exit') = undefined
+Alert: PASS: RadioButtonList.execEvent('full') = undefined
+Alert: PASS: RadioButtonList.execEvent('indexChange') = undefined
+Alert: PASS: RadioButtonList.execEvent('initialize') = undefined
+Alert: PASS: RadioButtonList.execEvent('mouseDown') = undefined
+Alert: PASS: RadioButtonList.execEvent('mouseEnter') = undefined
+Alert: PASS: RadioButtonList.execEvent('mouseExit') = undefined
+Alert: PASS: RadioButtonList.execEvent('mouseUp') = undefined
+Alert: PASS: RadioButtonList.execEvent('postOpen') = undefined
+Alert: PASS: RadioButtonList.execEvent('preOpen') = undefined
+Alert: PASS: RadioButtonList.execEvent('preSign') = undefined
+Alert: PASS: RadioButtonList.execEvent('validate') = undefined
+Alert: PASS: RadioButtonList.execInitialize('badarg') threw XFAObject.execInitialize: Incorrect number of parameters passed to function.
+Alert: PASS: RadioButtonList.execInitialize() = undefined
+Alert: PASS: RadioButtonList.execCalculate('badarg') threw XFAObject.execCalculate: Incorrect number of parameters passed to function.
+Alert: PASS: RadioButtonList.execCalculate() = undefined
+Alert: PASS: RadioButtonList.execValidate('badarg') threw XFAObject.execValidate: Incorrect number of parameters passed to function.
+Alert: PASS: RadioButtonList.execValidate() = true
+Alert: PASS: RadioButtonList.selectedMember('badarg') threw XFAObject.selectedMember: Incorrect number of parameters passed to function.
+Alert: PASS: RadioButtonList.selectedMember() = null
+Alert: PASS: RadioButtonList.defaultValue = undefined
+Alert: PASS: RadioButtonList.rawValue = null
+Alert: PASS: RadioButtonList.transient = undefined
+Alert: PASS: RadioButtonList.errorText = undefined
diff --git a/testing/resources/javascript/xfa_specific/xfa_field.in b/testing/resources/javascript/xfa_specific/xfa_field.in
new file mode 100644
index 0000000..a358921
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_field.in
@@ -0,0 +1,92 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+ {{streamlen}}
+>>
+stream
+<template>
+ <subform layout="tb" name="subform1">
+ <pageSet id="page" relation="orderedOccurrence">
+ <occur initial="1" max="1" min="1"/>
+ <pageArea id="Page1" name="Page1">
+ <occur max="1" min="1"/>
+ <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+ <medium long="297mm" short="210mm" stock="a4"/>
+ </pageArea>
+ </pageSet>
+ <subform layout="tb" name="subform2">
+ <occur initial="1" max="10" min="0" name="occur1">
+ </occur>
+ <field name="field1" h="10mm" w="40mm" x="10mm" y="12mm" border="solid">
+ <items>
+ <text>and a one</text>
+ <text>and a two</text>
+ </items>
+ <event activity="ready" ref="$form">
+ <script contentType="application/x-javascript"><![CDATA[
+ {{include ../expect.js}}
+ {{include ../property_test_helpers.js}}
+ var field = xfa.resolveNode("field1");
+ testRWProperty(field, "x", "10mm", "11mm");
+ testRWProperty(field, "y", "12mm", "13mm");
+ testRWProperty(field, "h", "10mm", "2in");
+ testRWProperty(field, "w", "40mm", "3in");
+ testRWProperty(field, "fontColor", "0,0,0", "42,62,4");
+ testRWProperty(field, "fillColor", "255,255,255", "41,61,11");
+ testRWProperty(field, "borderColor", "0,0,0", "241,161,11");
+ // TODO(tsepez): find a way to make this be defined.
+ // testRWProperty(field, "borderWidth", "1", "4");
+ testRWProperty(field, "mandatory", "disabled", "solid");
+ testRWProperty(field, "mandatoryMessage", "", "keep out");
+ testROProperty(field, "dataNode", "[object XFAObject]");
+ testROProperty(field, "length", 2);
+
+ expectError("field.execInitialize('phooey')");
+ expect("field.execInitialize()", undefined);
+
+ expectError("field.execEvent()");
+ expectError("field.execEvent(1, 2)");
+ expect("field.execEvent('validate')", true);
+
+ expectError("field.deleteItem()");
+ expectError("field.deleteItem(1, 2)");
+ expect("field.deleteItem(1)", true);
+ expect("field.deleteItem(137)", true); // silently ignored?
+
+ expectError("field.getSaveItem()");
+ expectError("field.getSaveItem(1, 2)");
+ expect("field.getSaveItem(0)", "and a one");
+ expect("field.getSaveItem(137)", null);
+
+ expectError("field.getItemState()");
+ expectError("field.getItemState(1, 2)");
+ expect("field.getItemState(0)", false);
+ expect("field.getItemState(1)", false);
+ expect("field.getItemState(137)", false);
+ expect("field.getItemState(-137)", false);
+
+ expectError("field.setItemState()");
+ expectError("field.setItemState(1, 2, 3)");
+ expect("field.setItemState(0, 1)", undefined);
+ expect("field.getItemState(0)", true);
+ expect("field.setItemState(0, 0)", undefined);
+ expect("field.getItemState(0)", false);
+
+ ]]></script>
+ </event>
+ </field>
+ </subform>
+ </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/xfa_field_expected.txt b/testing/resources/javascript/xfa_specific/xfa_field_expected.txt
new file mode 100644
index 0000000..645518a
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_field_expected.txt
@@ -0,0 +1,47 @@
+Alert: PASS: x = 10mm
+Alert: PASS: x = 11mm
+Alert: PASS: y = 12mm
+Alert: PASS: y = 13mm
+Alert: PASS: h = 10mm
+Alert: PASS: h = 2in
+Alert: PASS: w = 40mm
+Alert: PASS: w = 3in
+Alert: PASS: fontColor = 0,0,0
+Alert: PASS: fontColor = 42,62,4
+Alert: PASS: fillColor = 255,255,255
+Alert: PASS: fillColor = 41,61,11
+Alert: PASS: borderColor = 0,0,0
+Alert: PASS: borderColor = 241,161,11
+Alert: PASS: mandatory = disabled
+Alert: PASS: mandatory = solid
+Alert: PASS: mandatoryMessage =
+Alert: PASS: mandatoryMessage = keep out
+Alert: PASS: dataNode = [object XFAObject]
+Alert: PASS: dataNode threw Error: Invalid property set operation.
+Alert: PASS: length = 2
+Alert: PASS: length threw Error: Invalid property set operation.
+Alert: PASS: field.execInitialize('phooey') threw XFAObject.execInitialize: Incorrect number of parameters passed to function.
+Alert: PASS: field.execInitialize() = undefined
+Alert: PASS: field.execEvent() threw XFAObject.execEvent: Incorrect number of parameters passed to function.
+Alert: PASS: field.execEvent(1, 2) threw XFAObject.execEvent: Incorrect number of parameters passed to function.
+Alert: PASS: field.execEvent('validate') = true
+Alert: PASS: field.deleteItem() threw XFAObject.deleteItem: Incorrect number of parameters passed to function.
+Alert: PASS: field.deleteItem(1, 2) threw XFAObject.deleteItem: Incorrect number of parameters passed to function.
+Alert: PASS: field.deleteItem(1) = true
+Alert: PASS: field.deleteItem(137) = true
+Alert: PASS: field.getSaveItem() threw XFAObject.getSaveItem: Incorrect number of parameters passed to function.
+Alert: PASS: field.getSaveItem(1, 2) threw XFAObject.getSaveItem: Incorrect number of parameters passed to function.
+Alert: PASS: field.getSaveItem(0) = and a one
+Alert: PASS: field.getSaveItem(137) = null
+Alert: PASS: field.getItemState() threw XFAObject.getItemState: Incorrect number of parameters passed to function.
+Alert: PASS: field.getItemState(1, 2) threw XFAObject.getItemState: Incorrect number of parameters passed to function.
+Alert: PASS: field.getItemState(0) = false
+Alert: PASS: field.getItemState(1) = false
+Alert: PASS: field.getItemState(137) = false
+Alert: PASS: field.getItemState(-137) = false
+Alert: PASS: field.setItemState() threw XFAObject.setItemState: Incorrect number of parameters passed to function.
+Alert: PASS: field.setItemState(1, 2, 3) threw XFAObject.setItemState: Incorrect number of parameters passed to function.
+Alert: PASS: field.setItemState(0, 1) = undefined
+Alert: PASS: field.getItemState(0) = true
+Alert: PASS: field.setItemState(0, 0) = undefined
+Alert: PASS: field.getItemState(0) = false
diff --git a/testing/resources/javascript/xfa_specific/xfa_form.in b/testing/resources/javascript/xfa_specific/xfa_form.in
new file mode 100644
index 0000000..e21688f
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_form.in
@@ -0,0 +1,61 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+ {{streamlen}}
+>>
+stream
+<form>
+</form>
+<template>
+ <subform layout="tb" name="my_subform">
+ <pageSet id="page" relation="orderedOccurrence">
+ <occur initial="1" max="1" min="1"/>
+ <pageArea id="Page1" name="Page1">
+ <occur max="1" min="1"/>
+ <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+ </pageArea>
+ <pageArea id="Page2" name="Page2">
+ <occur max="1" min="1"/>
+ <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+ </pageArea>
+ </pageSet>
+ <event activity="docReady" ref="$host">
+ <script contentType="application/x-javascript">
+ {{include ../expect.js}}
+ {{include ../property_test_helpers.js}}
+ var my_form = xfa.resolveNode("#form");
+ testRWProperty(my_form, "checksum", "", "11");
+ expect("typeof my_form.execInitialize", "function");
+ expect("typeof my_form.execCalculate", "function");
+ expect("typeof my_form.execValidate", "function");
+ expectError("my_form.execInitialize('nonesuch')");
+ expect("my_form.execInitialize()", undefined);
+ expectError("my_form.execCalculate('nonesuch')");
+ expect("my_form.execCalculate()", undefined);
+ expectError("my_form.execValidate('nonesuch')");
+ expect("my_form.execValidate()", true);
+ expectError("my_form.formNodes()");
+ expectError("my_form.formNodes(1, 2)");
+ expectError("my_form.formNodes('nonesuch')");
+ expect("my_form.formNodes(my_subform)", "[object XFAObject]");
+ expectError("my_form.remerge(1)");
+ expect("my_form.remerge()", true);
+ expectError("my_form.recalculate()");
+ expectError("my_form.recalculate(1, 2)");
+ expect("my_form.recalculate(0)", undefined);
+ </script>
+ </event>
+ </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/xfa_form_expected.txt b/testing/resources/javascript/xfa_specific/xfa_form_expected.txt
new file mode 100644
index 0000000..191d702
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_form_expected.txt
@@ -0,0 +1,20 @@
+Alert: PASS: checksum =
+Alert: PASS: checksum = 11
+Alert: PASS: typeof my_form.execInitialize = function
+Alert: PASS: typeof my_form.execCalculate = function
+Alert: PASS: typeof my_form.execValidate = function
+Alert: PASS: my_form.execInitialize('nonesuch') threw XFAObject.execInitialize: Incorrect number of parameters passed to function.
+Alert: PASS: my_form.execInitialize() = undefined
+Alert: PASS: my_form.execCalculate('nonesuch') threw XFAObject.execCalculate: Incorrect number of parameters passed to function.
+Alert: PASS: my_form.execCalculate() = undefined
+Alert: PASS: my_form.execValidate('nonesuch') threw XFAObject.execValidate: Incorrect number of parameters passed to function.
+Alert: PASS: my_form.execValidate() = true
+Alert: PASS: my_form.formNodes() threw XFAObject.formNodes: Incorrect number of parameters passed to function.
+Alert: PASS: my_form.formNodes(1, 2) threw XFAObject.formNodes: Incorrect number of parameters passed to function.
+Alert: PASS: my_form.formNodes('nonesuch') threw XFAObject.formNodes: Incorrect parameter value.
+Alert: PASS: my_form.formNodes(my_subform) = [object XFAObject]
+Alert: PASS: my_form.remerge(1) threw XFAObject.remerge: Incorrect number of parameters passed to function.
+Alert: FAIL: my_form.remerge() = undefined, expected true
+Alert: PASS: my_form.recalculate() threw XFAObject.recalculate: Incorrect number of parameters passed to function.
+Alert: PASS: my_form.recalculate(1, 2) threw XFAObject.recalculate: Incorrect number of parameters passed to function.
+Alert: PASS: my_form.recalculate(0) = undefined
diff --git a/testing/resources/javascript/xfa_specific/xfa_globalobject.in b/testing/resources/javascript/xfa_specific/xfa_globalobject.in
new file mode 100644
index 0000000..be1f3be
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_globalobject.in
@@ -0,0 +1,41 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+ {{streamlen}}
+>>
+stream
+<template>
+ <subform layout="tb" name="my_doc">
+ <pageSet id="page" relation="orderedOccurrence">
+ <occur initial="1" max="1" min="1"/>
+ <pageArea id="Page1" name="Page1">
+ <occur max="1" min="1"/>
+ <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+ </pageArea>
+ <pageArea id="Page2" name="Page2">
+ <occur max="1" min="1"/>
+ <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+ </pageArea>
+ </pageSet>
+ <event activity="docReady" ref="$host">
+ <script contentType="application/x-javascript">
+ app.alert('We search non-xfa global space on missing prop: ' + (app == xfa.app));
+ app.alert('Global toString says ' + toString());
+ app.alert('toString.apply to bad object is ' + toString.apply(xfa));
+ app.alert('toString.apply to bad non-xfa object is ' + toString.apply(app));
+ </script>
+ </event>
+ </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/xfa_globalobject_expected.txt b/testing/resources/javascript/xfa_specific/xfa_globalobject_expected.txt
new file mode 100644
index 0000000..6384afe
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_globalobject_expected.txt
@@ -0,0 +1,4 @@
+Alert: We search non-xfa global space on missing prop: true
+Alert: Global toString says [object Root]
+Alert: toString.apply to bad object is [object Root]
+Alert: toString.apply to bad non-xfa object is [object Root]
diff --git a/testing/resources/javascript/xfa_specific/xfa_host_pseudomodel_expected.txt b/testing/resources/javascript/xfa_specific/xfa_host_pseudomodel_expected.txt
index 3835e4a..6c1359d 100644
--- a/testing/resources/javascript/xfa_specific/xfa_host_pseudomodel_expected.txt
+++ b/testing/resources/javascript/xfa_specific/xfa_host_pseudomodel_expected.txt
@@ -52,7 +52,7 @@
Alert: PASS: xfa.host.pageDown('ignored arg') = undefined
Alert: PASS: xfa.host.print(1, 2, 3, 4, 5, 6, 7) threw XFAObject.print: Incorrect number of parameters passed to function.
Alert: PASS: xfa.host.print(1, 2, 3, 4, 5, 6, 7, 8, 9) threw XFAObject.print: Incorrect number of parameters passed to function.
-Doc Print: 1, 1, 1, 2, 4, 8, 16, 32
+Doc Print: 1, 1, 1, 1, 1, 1, 1, 1
Alert: PASS: xfa.host.print(true, 1, 1, true, true, true, true, true) = undefined
Alert: PASS: xfa.host.response() threw XFAObject.response: Incorrect number of parameters passed to function.
Alert: PASS: xfa.host.response(1, 2, 3, 4, 5) threw XFAObject.response: Incorrect number of parameters passed to function.
diff --git a/testing/resources/javascript/xfa_specific/xfa_items.in b/testing/resources/javascript/xfa_specific/xfa_items.in
new file mode 100644
index 0000000..f1ee317
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_items.in
@@ -0,0 +1,196 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+ {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/2.6/">
+ <subform name="form1">
+ <pageSet>
+ <pageArea id="Page1" name="Page1">
+ <contentArea h="10.5in" w="8in" x="0.25in" y="0.25in"/>
+ <medium long="11in" short="8.5in" stock="letter"/>
+ </pageArea>
+ </pageSet>
+ <field h="9.0001mm" name="field1" w="47.625mm" x="6.35mm" y="92.075mm">
+ <ui>
+ <choiceList/>
+ </ui>
+ <items nonesuch="3">
+ <arc name="arc1"></arc>
+ <boolean name="bool0">0</boolean>
+ <boolean name="bool1">1</boolean>
+ <boolean name="boolbad">bad</boolean>
+ <boolean name="booltruenottrue">true</boolean>
+ <date name="date0"></date>
+ <date name="date1">2020-02-02</date>
+ <date name="date2">2039-12-01</date>
+ <date name="datebad">bad</date>
+ <dateTime name="datetime0"></dateTime>
+ <dateTime name="datetime1">2020-02-02T12:34:56</dateTime>
+ <dateTime name="datetime2">2039-12-01T12:34:56</dateTime>
+ <dateTime name="datetimebad">bad</dateTime>
+ <decimal name="decimal0"></decimal>
+ <decimal name="decimal1">42.0000000000000000001</decimal>
+ <decimal name="decimalbad">bad</decimal>
+ <exData name="ex0"></exData>
+ <exData name="ex1"><![CDATA[YZYZYZYZYZYZYZYZYZYZYZYZYZYZ]]></exData>
+ <float name="float0">-12.34</float>
+ <float name="float1">-12.34</float>
+ <float name="floatbad">bad</float>
+ <image name="image0">ABABABABABABABABA</image>
+ <image name="image1"><![CDATA[ABABABABABABABABA]]></image>
+ <integer name="int0"></integer>
+ <integer name="int1">1234</integer>
+ <integer name="intbad">bad</integer>
+ <line name="line0"></line>
+ <rectangle name="rect0"></rectangle>
+ <text name="text0"></text>
+ <text name="text1">Ahoy !!!</text>
+ <time name="time0"></time>
+ <time name="time1">12:34:56</time>
+ <goop name="goop0">Nonsense nodes not allowed here</goop>
+ </items>
+ </field>
+ <event activity="docReady">
+ <script contentType="application/x-javascript"><![CDATA[
+ {{include ../expect.js}}
+ {{include dump_tree.js}}
+
+ itemlist = xfa.resolveNode("form1.field1.#items");
+ dumpTree(itemlist);
+
+ arc1 = itemlist.resolveNode("arc1");
+
+ bool0 = itemlist.resolveNode("bool0");
+ expect("bool0.value", false);
+ bool0.value = 1;
+ expect("bool0.value", true);
+
+ bool1 = itemlist.resolveNode("bool1");
+ expect("bool1.value", true);
+ bool1.value = 0;
+ expect("bool1.value", false);
+
+ boolbad = itemlist.resolveNode("boolbad");
+ expect("boolbad.value", false);
+
+ booltruenottrue = itemlist.resolveNode("booltruenottrue");
+ expect("booltruenottrue.value", false);
+
+ // TODO(tsepez): confirm if this is correct.
+ booltruenottrue.value = true;
+ expect("booltruenottrue.value", false);
+
+ booltruenottrue.value = "zerp";
+ expect("booltruenottrue.value", false);
+ booltruenottrue.value = "1";
+ expect("booltruenottrue.value", true);
+ booltruenottrue.value = "10";
+ expect("booltruenottrue.value", true);
+ booltruenottrue.value = "1zerp";
+ expect("booltruenottrue.value", true);
+
+ // Date is just a node, and allows any text within.
+ date0 = itemlist.resolveNode("date0");
+ expect("date0.value", null);
+
+ date1 = itemlist.resolveNode("date1");
+ expect("date1.value", "2020-02-02");
+
+ date2 = itemlist.resolveNode("date2");
+ expect("date2.value", "2039-12-01");
+
+ datebad = itemlist.resolveNode("datebad");
+ expect("datebad.value", "bad");
+
+ // These are pretty much just nodes, and allow any text within.
+ // Just check that they parsed and that we can retrieve them.
+ datetime0 = itemlist.resolveNode("datetime0");
+ expect("datetime0", "[object XFAObject]");
+
+ datetime1 = itemlist.resolveNode("datetime1");
+ expect("datetime1", "[object XFAObject]");
+
+ datetime2 = itemlist.resolveNode("datetime2");
+ expect("datetime2", "[object XFAObject]");
+
+ datetimebad = itemlist.resolveNode("datetimebad");
+ expect("datetimebad", "[object XFAObject]");
+
+ decimal0 = itemlist.resolveNode("decimal0");
+ expect("decimal0", "[object XFAObject]");
+
+ decimal1 = itemlist.resolveNode("decimal1");
+ expect("decimal1", "[object XFAObject]");
+
+ decimalbad = itemlist.resolveNode("decimalbad");
+ expect("decimalbad", "[object XFAObject]");
+
+ ex0 = itemlist.resolveNode("ex0");
+ expect("ex0", "[object XFAObject]");
+
+ ex1 = itemlist.resolveNode("ex1");
+ expect("ex1", "[object XFAObject]");
+
+ float0 = itemlist.resolveNode("float0");
+ expect("float0", "[object XFAObject]");
+
+ float1 = itemlist.resolveNode("float1");
+ expect("float1", "[object XFAObject]");
+
+ floatbad = itemlist.resolveNode("floatbad");
+ expect("floatbad", "[object XFAObject]");
+
+ image0 = itemlist.resolveNode("image0");
+ expect("image0", "[object XFAObject]");
+
+ image1 = itemlist.resolveNode("image1");
+ expect("image1", "[object XFAObject]");
+
+ int0 = itemlist.resolveNode("int0");
+ expect("int0", "[object XFAObject]");
+
+ int1 = itemlist.resolveNode("int1");
+ expect("int1", "[object XFAObject]");
+
+ intbad = itemlist.resolveNode("intbad");
+ expect("intbad", "[object XFAObject]");
+
+ line0 = itemlist.resolveNode("line0");
+ expect("line0", "[object XFAObject]");
+
+ rect0 = itemlist.resolveNode("rect0");
+ expect("rect0", "[object XFAObject]");
+
+ text0 = itemlist.resolveNode("text0");
+ expect("text0", "[object XFAObject]");
+
+ text1 = itemlist.resolveNode("text1");
+ expect("text1", "[object XFAObject]");
+
+ time0 = itemlist.resolveNode("time0");
+ expect("time0", "[object XFAObject]");
+
+ time1 = itemlist.resolveNode("time1");
+ expect("time1", "[object XFAObject]");
+
+ // The parser is picky and won't let fake nodes in here.
+ goop0 = itemlist.resolveNode("goop0");
+ expect("goop0", null);
+ ]]></script>
+ </event>
+ </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/xfa_items_expected.txt b/testing/resources/javascript/xfa_specific/xfa_items_expected.txt
new file mode 100644
index 0000000..cc0c7c5
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_items_expected.txt
@@ -0,0 +1,80 @@
+Alert: items
+Alert: | arc
+Alert: | boolean
+Alert: | boolean
+Alert: | boolean
+Alert: | boolean
+Alert: | date
+Alert: | date
+Alert: | | #text
+Alert: | date
+Alert: | | #text
+Alert: | date
+Alert: | | #text
+Alert: | dateTime
+Alert: | dateTime
+Alert: | | #text
+Alert: | dateTime
+Alert: | | #text
+Alert: | dateTime
+Alert: | | #text
+Alert: | decimal
+Alert: | decimal
+Alert: | decimal
+Alert: | exData
+Alert: | exData
+Alert: | float
+Alert: | float
+Alert: | float
+Alert: | image
+Alert: | | #text
+Alert: | image
+Alert: | | #text
+Alert: | integer
+Alert: | integer
+Alert: | integer
+Alert: | line
+Alert: | rectangle
+Alert: | text
+Alert: | text
+Alert: | time
+Alert: | time
+Alert: PASS: bool0.value = false
+Alert: PASS: bool0.value = true
+Alert: PASS: bool1.value = true
+Alert: PASS: bool1.value = false
+Alert: PASS: boolbad.value = false
+Alert: PASS: booltruenottrue.value = false
+Alert: PASS: booltruenottrue.value = false
+Alert: PASS: booltruenottrue.value = false
+Alert: PASS: booltruenottrue.value = true
+Alert: PASS: booltruenottrue.value = true
+Alert: PASS: booltruenottrue.value = true
+Alert: PASS: date0.value = null
+Alert: PASS: date1.value = 2020-02-02
+Alert: PASS: date2.value = 2039-12-01
+Alert: PASS: datebad.value = bad
+Alert: PASS: datetime0 = [object XFAObject]
+Alert: PASS: datetime1 = [object XFAObject]
+Alert: PASS: datetime2 = [object XFAObject]
+Alert: PASS: datetimebad = [object XFAObject]
+Alert: PASS: decimal0 = [object XFAObject]
+Alert: PASS: decimal1 = [object XFAObject]
+Alert: PASS: decimalbad = [object XFAObject]
+Alert: PASS: ex0 = [object XFAObject]
+Alert: PASS: ex1 = [object XFAObject]
+Alert: PASS: float0 = [object XFAObject]
+Alert: PASS: float1 = [object XFAObject]
+Alert: PASS: floatbad = [object XFAObject]
+Alert: PASS: image0 = [object XFAObject]
+Alert: PASS: image1 = [object XFAObject]
+Alert: PASS: int0 = [object XFAObject]
+Alert: PASS: int1 = [object XFAObject]
+Alert: PASS: intbad = [object XFAObject]
+Alert: PASS: line0 = [object XFAObject]
+Alert: PASS: rect0 = [object XFAObject]
+Alert: PASS: text0 = [object XFAObject]
+Alert: PASS: text1 = [object XFAObject]
+Alert: PASS: time0 = [object XFAObject]
+Alert: PASS: time1 = [object XFAObject]
+Alert: PASS: goop0 = null
diff --git a/testing/resources/javascript/xfa_specific/xfa_log_pseudomodel.in b/testing/resources/javascript/xfa_specific/xfa_log_pseudomodel.in
index 2de417e..a3f28fc 100644
--- a/testing/resources/javascript/xfa_specific/xfa_log_pseudomodel.in
+++ b/testing/resources/javascript/xfa_specific/xfa_log_pseudomodel.in
@@ -40,6 +40,7 @@
endobj
{{include ../../xfa_locale_6_0.fragment}}
{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
{{xref}}
{{trailer}}
{{startxref}}
diff --git a/testing/resources/javascript/xfa_specific/xfa_node.in b/testing/resources/javascript/xfa_specific/xfa_node.in
index 7157993..a0e9b25 100644
--- a/testing/resources/javascript/xfa_specific/xfa_node.in
+++ b/testing/resources/javascript/xfa_specific/xfa_node.in
@@ -7,6 +7,7 @@
{{streamlen}}
>>
stream
+<goop viscosity="sludge">Spurious Nonsense</goop>
<template>
<subform layout="tb" name="my_doc">
<pageSet id="page" relation="orderedOccurrence">
@@ -21,9 +22,13 @@
</pageArea>
</pageSet>
<event activity="docReady" ref="$host">
- <script contentType="application/x-javascript">
+ <script contentType="application/x-javascript"><![CDATA[
{{include ../expect.js}}
{{include ../property_test_helpers.js}}
+ {{include dump_tree.js}}
+
+ dumpTree(xfa);
+
testROProperty(my_doc, "isContainer", true);
testROProperty(my_doc, "isNull", false);
testROProperty(my_doc, "model", "[object XFAObject]");
@@ -74,7 +79,35 @@
// Test free-form attributes outside of the XFA schema.
expect("my_doc.setAttribute('fake_value', 'fake_attr')", undefined);
expect("my_doc.getAttribute('fake_attr')", 'fake_value');
- </script>
+
+ // Test magic "xfa" property bound to all nodes.
+ expect("my_doc.xfa == xfa", true);
+
+ // Test "packet" nodes which are unrecognized tags at XDP level.
+ expect("goop", undefined);
+ var goop = this.resolveNode('#packet');
+ expect("goop.className", "packet");
+ expect("goop.viscosity", undefined);
+ expectError("goop.getAttribute()");
+ expectError("goop.getAttribute(1, 2)");
+ expect("goop.getAttribute('nonesuch')", "");
+ expect("goop.getAttribute('viscosity')", "sludge");
+ expectError("goop.setAttribute()");
+ expectError("goop.setAttribute(1)");
+ expectError("goop.setAttribute(1, 2, 3)");
+
+ // Remember, args to setAttribute() are backwards from what one might
+ // typically expect ...
+ expect("goop.setAttribute(7, 'ph')", undefined);
+ expect("goop.getAttribute('ph')", 7);
+
+ expect("goop.content", "Spurious Nonsense");
+ expect("goop.content = ' for All'", " for All");
+
+ // TODO(tsepez): assigning new content seems to append although it
+ // wasn't included in the return value of the assignment above.
+ expect("goop.content", "Spurious Nonsense for All");
+ ]]></script>
</event>
</subform>
</template>
diff --git a/testing/resources/javascript/xfa_specific/xfa_node_expected.txt b/testing/resources/javascript/xfa_specific/xfa_node_expected.txt
index 68b73a9..8455004 100644
--- a/testing/resources/javascript/xfa_specific/xfa_node_expected.txt
+++ b/testing/resources/javascript/xfa_specific/xfa_node_expected.txt
@@ -1,3 +1,200 @@
+Alert: xfa
+Alert: | config
+Alert: | | agent
+Alert: | | | destination
+Alert: | | | | #text
+Alert: | | | pdf
+Alert: | | | | fontInfo
+Alert: | | present
+Alert: | | | pdf
+Alert: | | | | version
+Alert: | | | | | #text
+Alert: | | | | adobeExtensionLevel
+Alert: | | | | | #text
+Alert: | | | | renderPolicy
+Alert: | | | | | #text
+Alert: | | | | scriptModel
+Alert: | | | | | #text
+Alert: | | | | interactive
+Alert: | | | | | #text
+Alert: | | | xdp
+Alert: | | | | packets
+Alert: | | | | | #text
+Alert: | | | destination
+Alert: | | | | #text
+Alert: | | | script
+Alert: | | | | #text
+Alert: | | acrobat
+Alert: | | | acrobat7
+Alert: | | | | dynamicRender
+Alert: | | | | | #text
+Alert: | | | validate
+Alert: | | | | #text
+Alert: | packet
+Alert: | template
+Alert: | | subform
+Alert: | | | event
+Alert: | | | | script
+Alert: | | | | | #text
+Alert: | localeSet
+Alert: | | locale
+Alert: | | | calendarSymbols
+Alert: | | | | monthNames
+Alert: | | | | | month
+Alert: | | | | | | #text
+Alert: | | | | | month
+Alert: | | | | | | #text
+Alert: | | | | | month
+Alert: | | | | | | #text
+Alert: | | | | | month
+Alert: | | | | | | #text
+Alert: | | | | | month
+Alert: | | | | | | #text
+Alert: | | | | | month
+Alert: | | | | | | #text
+Alert: | | | | | month
+Alert: | | | | | | #text
+Alert: | | | | | month
+Alert: | | | | | | #text
+Alert: | | | | | month
+Alert: | | | | | | #text
+Alert: | | | | | month
+Alert: | | | | | | #text
+Alert: | | | | | month
+Alert: | | | | | | #text
+Alert: | | | | | month
+Alert: | | | | | | #text
+Alert: | | | | monthNames
+Alert: | | | | | month
+Alert: | | | | | | #text
+Alert: | | | | | month
+Alert: | | | | | | #text
+Alert: | | | | | month
+Alert: | | | | | | #text
+Alert: | | | | | month
+Alert: | | | | | | #text
+Alert: | | | | | month
+Alert: | | | | | | #text
+Alert: | | | | | month
+Alert: | | | | | | #text
+Alert: | | | | | month
+Alert: | | | | | | #text
+Alert: | | | | | month
+Alert: | | | | | | #text
+Alert: | | | | | month
+Alert: | | | | | | #text
+Alert: | | | | | month
+Alert: | | | | | | #text
+Alert: | | | | | month
+Alert: | | | | | | #text
+Alert: | | | | | month
+Alert: | | | | | | #text
+Alert: | | | | dayNames
+Alert: | | | | | day
+Alert: | | | | | | #text
+Alert: | | | | | day
+Alert: | | | | | | #text
+Alert: | | | | | day
+Alert: | | | | | | #text
+Alert: | | | | | day
+Alert: | | | | | | #text
+Alert: | | | | | day
+Alert: | | | | | | #text
+Alert: | | | | | day
+Alert: | | | | | | #text
+Alert: | | | | | day
+Alert: | | | | | | #text
+Alert: | | | | dayNames
+Alert: | | | | | day
+Alert: | | | | | | #text
+Alert: | | | | | day
+Alert: | | | | | | #text
+Alert: | | | | | day
+Alert: | | | | | | #text
+Alert: | | | | | day
+Alert: | | | | | | #text
+Alert: | | | | | day
+Alert: | | | | | | #text
+Alert: | | | | | day
+Alert: | | | | | | #text
+Alert: | | | | | day
+Alert: | | | | | | #text
+Alert: | | | | meridiemNames
+Alert: | | | | | meridiem
+Alert: | | | | | | #text
+Alert: | | | | | meridiem
+Alert: | | | | | | #text
+Alert: | | | | eraNames
+Alert: | | | | | era
+Alert: | | | | | | #text
+Alert: | | | | | era
+Alert: | | | | | | #text
+Alert: | | | datePatterns
+Alert: | | | | datePattern
+Alert: | | | | | #text
+Alert: | | | | datePattern
+Alert: | | | | | #text
+Alert: | | | | datePattern
+Alert: | | | | | #text
+Alert: | | | | datePattern
+Alert: | | | | | #text
+Alert: | | | timePatterns
+Alert: | | | | timePattern
+Alert: | | | | | #text
+Alert: | | | | timePattern
+Alert: | | | | | #text
+Alert: | | | | timePattern
+Alert: | | | | | #text
+Alert: | | | | timePattern
+Alert: | | | | | #text
+Alert: | | | dateTimeSymbols
+Alert: | | | | #text
+Alert: | | | numberPatterns
+Alert: | | | | numberPattern
+Alert: | | | | | #text
+Alert: | | | | numberPattern
+Alert: | | | | | #text
+Alert: | | | | numberPattern
+Alert: | | | | | #text
+Alert: | | | numberSymbols
+Alert: | | | | numberSymbol
+Alert: | | | | | #text
+Alert: | | | | numberSymbol
+Alert: | | | | | #text
+Alert: | | | | numberSymbol
+Alert: | | | | | #text
+Alert: | | | | numberSymbol
+Alert: | | | | | #text
+Alert: | | | | numberSymbol
+Alert: | | | | | #text
+Alert: | | | currencySymbols
+Alert: | | | | currencySymbol
+Alert: | | | | | #text
+Alert: | | | | currencySymbol
+Alert: | | | | | #text
+Alert: | | | | currencySymbol
+Alert: | | | | | #text
+Alert: | | | typefaces
+Alert: | | | | typeface
+Alert: | | | | typeface
+Alert: | | | | typeface
+Alert: | | | | typeface
+Alert: | | | | typeface
+Alert: | | | | typeface
+Alert: | | | | typeface
+Alert: | | | | typeface
+Alert: | | | | typeface
+Alert: | | | | typeface
+Alert: | | | | typeface
+Alert: | | | | typeface
+Alert: | dataModel
+Alert: | | dataGroup
+Alert: | | | dataGroup
+Alert: | form
+Alert: | | subform
+Alert: | | | event
+Alert: | | | | script
+Alert: | | | | | #text
Alert: PASS: isContainer = true
Alert: PASS: isContainer threw Error: Invalid property set operation.
Alert: PASS: isNull = false
@@ -49,3 +246,19 @@
Alert: PASS: my_doc.getAttribute('ns') = something
Alert: PASS: my_doc.setAttribute('fake_value', 'fake_attr') = undefined
Alert: PASS: my_doc.getAttribute('fake_attr') = fake_value
+Alert: PASS: my_doc.xfa == xfa = true
+Alert: PASS: goop = undefined
+Alert: PASS: goop.className = packet
+Alert: PASS: goop.viscosity = undefined
+Alert: PASS: goop.getAttribute() threw XFAObject.getAttribute: Incorrect number of parameters passed to function.
+Alert: PASS: goop.getAttribute(1, 2) threw XFAObject.getAttribute: Incorrect number of parameters passed to function.
+Alert: PASS: goop.getAttribute('nonesuch') =
+Alert: PASS: goop.getAttribute('viscosity') = sludge
+Alert: PASS: goop.setAttribute() threw XFAObject.setAttribute: Incorrect number of parameters passed to function.
+Alert: PASS: goop.setAttribute(1) threw XFAObject.setAttribute: Incorrect number of parameters passed to function.
+Alert: PASS: goop.setAttribute(1, 2, 3) threw XFAObject.setAttribute: Incorrect number of parameters passed to function.
+Alert: PASS: goop.setAttribute(7, 'ph') = null
+Alert: PASS: goop.getAttribute('ph') = 7
+Alert: PASS: goop.content = Spurious Nonsense
+Alert: PASS: goop.content = ' for All' = for All
+Alert: PASS: goop.content = Spurious Nonsense for All
diff --git a/testing/resources/javascript/xfa_specific/xfa_template.in b/testing/resources/javascript/xfa_specific/xfa_template.in
new file mode 100644
index 0000000..60ad46d
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_template.in
@@ -0,0 +1,60 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+ {{streamlen}}
+>>
+stream
+<template>
+ <subform layout="tb" name="my_doc">
+ <pageSet id="page" relation="orderedOccurrence">
+ <occur initial="1" max="1" min="1"/>
+ <pageArea id="Page1" name="Page1">
+ <occur max="1" min="1"/>
+ <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+ </pageArea>
+ <pageArea id="Page2" name="Page2">
+ <occur max="1" min="1"/>
+ <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+ </pageArea>
+ </pageSet>
+ <event activity="docReady" ref="$host">
+ <script contentType="application/x-javascript"><![CDATA[
+ {{include ../expect.js}}
+ template0 = xfa.resolveNode("#template");
+ expect("template0.className", "template");
+
+ expectError("template0.execCalculate(1)");
+ expect("template0.execCalculate()", false);
+
+ expectError("template0.execInitialize(1)", false);
+ expect("template0.execInitialize()", false);
+
+ expectError("template0.execValidate(1)", false);
+ expect("template0.execValidate()", false);
+
+ expectError("template0.formNodes()", undefined);
+ expectError("template0.formNodes(1, 2)", undefined);
+ expect("template0.formNodes(true)", true);
+
+ expectError("template0.recalculate()", undefined);
+ expectError("template0.recalculate(1, 2)", undefined);
+ expect("template0.recalculate(true)", true);
+
+ expectError("template0.remerge(1)", undefined);
+ expect("template0.remerge()", undefined);
+ ]]></script>
+ </event>
+ </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/xfa_template_expected.txt b/testing/resources/javascript/xfa_specific/xfa_template_expected.txt
new file mode 100644
index 0000000..9bb1ffd
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_template_expected.txt
@@ -0,0 +1,15 @@
+Alert: PASS: template0.className = template
+Alert: PASS: template0.execCalculate(1) threw XFAObject.execCalculate: Incorrect number of parameters passed to function.
+Alert: PASS: template0.execCalculate() = false
+Alert: PASS: template0.execInitialize(1) threw XFAObject.execInitialize: Incorrect number of parameters passed to function.
+Alert: PASS: template0.execInitialize() = false
+Alert: PASS: template0.execValidate(1) threw XFAObject.execValidate: Incorrect number of parameters passed to function.
+Alert: PASS: template0.execValidate() = false
+Alert: PASS: template0.formNodes() threw XFAObject.formNodes: Incorrect number of parameters passed to function.
+Alert: PASS: template0.formNodes(1, 2) threw XFAObject.formNodes: Incorrect number of parameters passed to function.
+Alert: PASS: template0.formNodes(true) = true
+Alert: PASS: template0.recalculate() threw XFAObject.recalculate: Incorrect number of parameters passed to function.
+Alert: PASS: template0.recalculate(1, 2) threw XFAObject.recalculate: Incorrect number of parameters passed to function.
+Alert: PASS: template0.recalculate(true) = true
+Alert: PASS: template0.remerge(1) threw XFAObject.remerge: Incorrect number of parameters passed to function.
+Alert: PASS: template0.remerge() = undefined
diff --git a/testing/resources/javascript/xfa_specific/xfa_variables.in b/testing/resources/javascript/xfa_specific/xfa_variables.in
new file mode 100644
index 0000000..6bfdb97
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_variables.in
@@ -0,0 +1,76 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+ {{streamlen}}
+>>
+stream
+<template>
+ <subform layout="tb" name="my_doc">
+ <variables>
+ <text name="xx01">123</text>
+ <text name="xx02">456</text>
+ <integer name="xx03">123</integer>
+ <integer name="xx04">456</integer>
+ </variables>
+ <pageSet id="page" relation="orderedOccurrence">
+ <occur initial="1" max="1" min="1"/>
+ <pageArea id="Page1" name="Page1">
+ <occur max="1" min="1"/>
+ <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+ </pageArea>
+ <pageArea id="Page2" name="Page2">
+ <occur max="1" min="1"/>
+ <contentArea h="100mm" w="200mm" x="0.25in" y="0.25in"/>
+ </pageArea>
+ </pageSet>
+ <event activity="docReady" ref="$host">
+ <script name="my_script" contentType="application/x-javascript"><![CDATA[
+ {{include ../expect.js}}
+ try {
+ var script1 = xfa.resolveNode('template..my_script');
+ var script2 = xfa.resolveNode('template..their_script');
+ var script3 = xfa.resolveNode('template..other_script');
+ app.alert('First, poke at a script node itsef');
+ expect('script1.stateless', '0');
+ expectError('script1.stateless = 42');
+ app.alert('We search variables context ' + (xx01.value + xx02.value));
+ app.alert('We search variables context ' + (xx03.value + xx04.value));
+ app.alert('We resolve off of script1 ' + (script1.xx01.value + script1.xx02.value));
+ app.alert('We resolve off of script2 ' + (script2.xx01.value + script2.xx02.value));
+ app.alert('We resolve off of script3 ' + (script3.xx01.value + script3.xx02.value));
+ app.alert('We resolve off of script1 ' + script1.nonesuch);
+ app.alert('We resolve off of script2 ' + script2.nonesuch);
+ app.alert('We resolve off of script3 ' + script3.nonesuch);
+ } catch (e) {
+ app.alert('Error: ' + e);
+ }
+ ]]></script>
+ </event>
+ </subform>
+ <subform layout="tb" name="their_doc">
+ <variables>
+ <text name="xx01">78</text>
+ <text name="xx02">90</text>
+ <integer name="xx03">78</integer>
+ <integer name="xx04">90</integer>
+ <script name="other_script">
+ var xx01 = "chips";
+ </script>
+ </variables>
+ <script name="their_script">
+ var xx01 = "clams";
+ </script>
+ </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/xfa_specific/xfa_variables_expected.txt b/testing/resources/javascript/xfa_specific/xfa_variables_expected.txt
new file mode 100644
index 0000000..75a457f
--- /dev/null
+++ b/testing/resources/javascript/xfa_specific/xfa_variables_expected.txt
@@ -0,0 +1,11 @@
+Alert: First, poke at a script node itsef
+Alert: PASS: script1.stateless = 0
+Alert: PASS: script1.stateless = 42 threw Error: Invalid property set operation.
+Alert: We search variables context 123456
+Alert: We search variables context 579
+Alert: We resolve off of script1 123456
+Alert: We resolve off of script2 7890
+Alert: We resolve off of script3 7890
+Alert: We resolve off of script1 undefined
+Alert: We resolve off of script2 undefined
+Alert: We resolve off of script3 undefined
diff --git a/testing/resources/jpx_lzw.in b/testing/resources/jpx_lzw.in
new file mode 100644
index 0000000..46914c1
--- /dev/null
+++ b/testing/resources/jpx_lzw.in
@@ -0,0 +1,55 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /MediaBox [0 0 612 792]
+ /Resources <<
+ /XObject <<
+ /Im0 5 0 R
+ >>
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+612 0 0 792 0 0 cm
+/Im0 Do
+Q
+endstream
+endobj
+{{object 5 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /Filter [/ASCIIHexDecode /LZWDecode /JPXDecode]
+ /Width 612
+ /Height 792
+ {{streamlen}}
+>>
+stream
+80002040c351404020068290e0a8100028663a1e4e06a380c8410d004522d0d16c68d10d0b1a4d06
+439408061881008c8000181c0f180003cc66f361c80330084358d1a31bfc9eff288005f189549a51
+30a24ae5947a44b00e0100d3ea3507f948000c86d400a040200002ff2e0002640201209050b35a2c
+f69b4bfe5625af10ce465309d0ca6410188f220279c0ca6e251408a47101d8ca72399a4de6e100c8
+5c33170c1fe9000432303faf3fd26cf5a14022502a2bdfe7f369a0b480980c12097d1801be801c05
+0005c57bf818daa6ee5fed9801
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/jpx_lzw.pdf b/testing/resources/jpx_lzw.pdf
new file mode 100644
index 0000000..8735018
--- /dev/null
+++ b/testing/resources/jpx_lzw.pdf
@@ -0,0 +1,67 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /MediaBox [0 0 612 792]
+ /Resources <<
+ /XObject <<
+ /Im0 5 0 R
+ >>
+ >>
+>>
+endobj
+4 0 obj <<
+ /Length 31
+>>
+stream
+q
+612 0 0 792 0 0 cm
+/Im0 Do
+Q
+endstream
+endobj
+5 0 obj <<
+ /Type /XObject
+ /Subtype /Image
+ /Filter [/ASCIIHexDecode /LZWDecode /JPXDecode]
+ /Width 612
+ /Height 792
+ /Length 432
+>>
+stream
+80002040c351404020068290e0a8100028663a1e4e06a380c8410d004522d0d16c68d10d0b1a4d06
+439408061881008c8000181c0f180003cc66f361c80330084358d1a31bfc9eff288005f189549a51
+30a24ae5947a44b00e0100d3ea3507f948000c86d400a040200002ff2e0002640201209050b35a2c
+f69b4bfe5625af10ce465309d0ca6410188f220279c0ca6e251408a47101d8ca72399a4de6e100c8
+5c33170c1fe9000432303faf3fd26cf5a14022502a2bdfe7f369a0b480980c12097d1801be801c05
+0005c57bf818daa6ee5fed9801
+endstream
+endobj
+xref
+0 6
+0000000000 65535 f
+0000000015 00000 n
+0000000068 00000 n
+0000000131 00000 n
+0000000287 00000 n
+0000000369 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 6
+>>
+startxref
+965
+%%EOF
diff --git a/testing/resources/js.pdf b/testing/resources/js.pdf
index 07e9b75..8d64866 100644
--- a/testing/resources/js.pdf
+++ b/testing/resources/js.pdf
@@ -22,7 +22,6 @@
4 0 obj <<
/Names [
(normal) 5 0 R
- (encoded_subtype) 6 0 R
(no_type) 7 0 R
(wrongtype) 8 0 R
(wrongsubtype) 9 0 R
@@ -36,12 +35,6 @@
/JS 11 0 R
>>
endobj
-6 0 obj <<
- /Type /Action
- /S /J#61v#61Script
- /JS 11 0 R
->>
-endobj
7 0 obj <<
/S /JavaScript
/JS 12 0 R
diff --git a/testing/resources/line_annot.in b/testing/resources/line_annot.in
new file mode 100644
index 0000000..0778169
--- /dev/null
+++ b/testing/resources/line_annot.in
@@ -0,0 +1,50 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 612 792]
+ /Annots [
+ 4 0 R 5 0 R
+ ]
+ /Tabs /R
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Annot
+ /Subtype /Line
+ /NM (Line-1)
+ /F 4
+ /L [159 296 472 243.42]
+ /P 3 0 R
+ /C [1 0.90196 0]
+ /Rect [293 530 349 542]
+ /Border [0.25 0.5 2]
+>>
+endobj
+{{object 5 0}} <<
+ /Type /Annot
+ /Subtype /Line
+ /NM (Line-2)
+ /F 4
+ /L [159 296 472]
+ /P 3 0 R
+ /C [1 0.90196 0]
+ /Rect [293 530 349 542]
+ /Border [0.25 0.5]
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/line_annot.pdf b/testing/resources/line_annot.pdf
new file mode 100644
index 0000000..7a7fee3
--- /dev/null
+++ b/testing/resources/line_annot.pdf
@@ -0,0 +1,62 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 612 792]
+ /Annots [
+ 4 0 R 5 0 R
+ ]
+ /Tabs /R
+>>
+endobj
+4 0 obj <<
+ /Type /Annot
+ /Subtype /Line
+ /NM (Line-1)
+ /F 4
+ /L [159 296 472 243.42]
+ /P 3 0 R
+ /C [1 0.90196 0]
+ /Rect [293 530 349 542]
+ /Border [0.25 0.5 2]
+>>
+endobj
+5 0 obj <<
+ /Type /Annot
+ /Subtype /Line
+ /NM (Line-2)
+ /F 4
+ /L [159 296 472]
+ /P 3 0 R
+ /C [1 0.90196 0]
+ /Rect [293 530 349 542]
+ /Border [0.25 0.5]
+>>
+endobj
+xref
+0 6
+0000000000 65535 f
+0000000015 00000 n
+0000000068 00000 n
+0000000131 00000 n
+0000000251 00000 n
+0000000431 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 6
+>>
+startxref
+602
+%%EOF
diff --git a/testing/resources/link_annots.in b/testing/resources/link_annots.in
deleted file mode 100644
index 076069c..0000000
--- a/testing/resources/link_annots.in
+++ /dev/null
@@ -1,333 +0,0 @@
-{{header}}
-{{object 1 0}} <<
- /Type /Catalog
- /Pages 2 0 R
->>
-endobj
-{{object 2 0}} <<
- /Type /Pages
- /Count 2
- /Kids [3 0 R 4 0 R]
- /MediaBox [0 0 612 792]
- /CropBox [0 0 612 792]
- /Resources <<
- /Font <<
- /F1 7 0 R
- /F2 8 0 R
- >>
- /ProcSet [/PDF /Text /ImageC]
- /ExtGState <<
- /GS0 23 0 R
- >>
- >>
->>
-endobj
-{{object 3 0}} <<
- /Type /Page
- /Parent 2 0 R
- /Contents 5 0 R
- /Annots [15 0 R 16 0 R 17 0 R 18 0 R 19 0 R 20 0 R 21 0 R 22 0 R]
->>
-endobj
-{{object 4 0}} <<
- /Type /Page
- /Parent 2 0 R
- /Contents 6 0 R
- /Annots [15 0 R 16 0 R]
->>
-endobj
-{{object 5 0}} <<
- {{streamlen}}
->>
-stream
-BT
-70 700 Td
-/F1 18 Tf
-(Link Annotations - Page 1) Tj
-0 -65 Td
-/F2 14 Tf
-(1. Link with destination to first page) Tj
-10 -20 Td
-/F2 14 Tf
-(2. Link with destination to second page) Tj
--12 -84 Td
-/F2 10 Tf
-(PDF Reference, Version 1.7, Section 8.4.5 defines Annotations) Tj
-2 -53 Td
-(3. An example of Highlight with text notes) Tj
-0 -18 Td
-(https://pdfium.googlesource.com/pdfium is link in plain text, not link annotation. These are referred to) Tj
-0 -17 Td
-(as WebLinks in PDFium.)Tj
-ET
-endstream
-endobj
-{{object 6 0}} <<
- {{streamlen}}
->>
-stream
-BT
-70 700 Td
-/F1 18 Tf
-(Link Annotations - Page 2) Tj
-0 -65 Td
-/F2 14 Tf
-(1. Link with destination to first page) Tj
-10 -20 Td
-/F2 14 Tf
-(2. Link with destination to second page) Tj
-ET
-endstream
-endobj
-{{object 7 0}} <<
- /Type /Font
- /Subtype /Type1
- /BaseFont /Times-Roman
->>
-endobj
-{{object 8 0}} <<
- /Type /Font
- /Subtype /Type1
- /BaseFont /Helvetica
->>
-endobj
-{{object 9 0}} <<
- /Type /XObject
- /Subtype /Form
- /FormType 1
- {{streamlen}}
- /BBox [293 530 349 542]
- /Resources <<
- /XObject <<
- /Form0 10 0 R
- >>
- /ExtGState <<
- /GS0 24 0 R
- >>
- >>
->>
-stream
-/GS0 gs
-/Form0 Do
-endstream
-endobj
-{{object 10 0}} <<
- /Type /XObject
- /Subtype /Form
- /FormType 1
- /Group <<
- /S /Transparency
- >>
- {{streamlen}}
- /BBox [293 530 349 542]
->>
-stream
-1.0 1.0 0.0 rg
-293 530 m
-349 530 l
-349 542 l
-293 542 l
-h f
-endstream
-endobj
-{{object 11 0}} <<
- /Type /XObject
- /Subtype /Form
- /FormType 1
- {{streamlen}}
- /BBox [83 440 178 453]
- /Resources <<
- /XObject <<
- /Form0 12 0 R
- >>
- /ExtGState <<
- /GS0 24 0 R
- >>
- >>
->>
-stream
-/GS0 gs
-/Form0 Do
-endstream
-endobj
-{{object 12 0}} <<
- /Type /XObject
- /Subtype /Form
- /FormType 1
- /Group <<
- /S /Transparency
- >>
- {{streamlen}}
- /BBox [83 440 178 453]
->>
-stream
-0.0 1.0 1.0 rg
-83 440 m
-178 440 l
-178 453 l
-83 453 l
-h f
-endstream
-endobj
-{{object 13 0}} <<
- /Type /XObject
- /Subtype /Form
- /FormType 1
- {{streamlen}}
- /BBox [149 476 191 487]
- /Resources <<
- /XObject <<
- /Form0 14 0 R
- >>
- /ExtGState <<
- /GS0 24 0 R
- >>
- >>
->>
-stream
-/GS0 gs
-/Form0 Do
-endstream
-endobj
-{{object 14 0}} <<
- /Type /XObject
- /Subtype /Form
- /FormType 1
- /Group <<
- /S /Transparency
- >>
- {{streamlen}}
- /BBox [149 476 191 487]
->>
-stream
-0.0 1.0 0.0 rg
-149 476 m
-191 476 l
-191 487 l
-149 487 l
-h f
-endstream
-endobj
-{{object 15 0}} <<
- /Type /Annot
- /Subtype /Link
- /BS <<
- /W 0
- >>
- /Rect [69 633 542 653]
- /Dest [3 0 R /XYZ 200 725 0]
- /F 4
->>
-endobj
-{{object 16 0}} <<
- /Type /Annot
- /Subtype /Link
- /BS <<
- /W 0
- >>
- /Rect [80 613 542 633]
- /Dest [4 0 R /XYZ 200 725 0]
- /F 4
->>
-endobj
-{{object 17 0}} <<
- /Type /Annot
- /Subtype /Link
- /BS <<
- /W 0
- >>
- /Rect [66 529 196 544]
- /A <<
- /Type /Action
- /URI (https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/pdf_reference_1-7.pdf)
- /S /URI
- >>
- /F 4
->>
-endobj
-{{object 18 0}} <<
- /Type /Annot
- /Subtype /Link
- /BS <<
- /W 0
- >>
- /Rect [83 440 178 453]
- /QuadPoints [83 453 178 453 83 440 178 440]
- /A <<
- /Type /Action
- /URI (https://cs.chromium.org/chromium/src/third_party/pdfium/public/fpdf_text.h)
- /S /URI
- >>
- /F 4
->>
-endobj
-{{object 19 0}} <<
- /Type /Annot
- /Subtype /Highlight
- /AP <<
- /N 9 0 R
- >>
- /NM (Highlight-1)
- /F 4
- /QuadPoints [293 542 349 542 293 530 349 530]
- /P 3 0 R
- /C [1 0.90196 0]
- /Rect [293 530 349 542]
->>
-endobj
-{{object 20 0}} <<
- /Type /Annot
- /Subtype /Highlight
- /AP <<
- /N 11 0 R
- >>
- /NM (Highlight-2)
- /F 4
- /QuadPoints [83 453 178 453 83 440 178 440]
- /P 3 0 R
- /C [0.26667 0.78431 0.96078]
- /Rect [83 440 178 453]
->>
-endobj
-{{object 21 0}} <<
- /Type /Annot
- /Subtype /Popup
- /Parent 22 0 R
- /Rect [191 377 443 488]
->>
-endobj
-{{object 22 0}} <<
- /Type /Annot
- /Subtype /Highlight
- /Popup 21 0 R
- /AP <<
- /N 13 0 R
- >>
- /NM (Highlight-With-Popup-1)
- /Contents (Text Note)
- /QuadPoints [149 487 191 487 149 476 191 476]
- /P 3 0 R
- /C [0.14902 0.90196 0]
- /Rect [149 476 191 487]
- /F 4
->>
-endobj
-{{object 23 0}} <<
- /ca 1
- /Type /ExtGState
- /CA 1
- /BM /Normal
->>
-endobj
-{{object 24 0}} <<
- /ca 1
- /Type /ExtGState
- /CA 1
- /AIS false
- /BM /Multiply
->>
-endobj
-{{xref}}
-{{trailer}}
-{{startxref}}
-%%EOF
diff --git a/testing/resources/link_annots.pdf b/testing/resources/link_annots.pdf
deleted file mode 100644
index b964bf5..0000000
--- a/testing/resources/link_annots.pdf
+++ /dev/null
@@ -1,364 +0,0 @@
-%PDF-1.7
-% ò¤ô
-1 0 obj <<
- /Type /Catalog
- /Pages 2 0 R
->>
-endobj
-2 0 obj <<
- /Type /Pages
- /Count 2
- /Kids [3 0 R 4 0 R]
- /MediaBox [0 0 612 792]
- /CropBox [0 0 612 792]
- /Resources <<
- /Font <<
- /F1 7 0 R
- /F2 8 0 R
- >>
- /ProcSet [/PDF /Text /ImageC]
- /ExtGState <<
- /GS0 23 0 R
- >>
- >>
->>
-endobj
-3 0 obj <<
- /Type /Page
- /Parent 2 0 R
- /Contents 5 0 R
- /Annots [15 0 R 16 0 R 17 0 R 18 0 R 19 0 R 20 0 R 21 0 R 22 0 R]
->>
-endobj
-4 0 obj <<
- /Type /Page
- /Parent 2 0 R
- /Contents 6 0 R
- /Annots [15 0 R 16 0 R]
->>
-endobj
-5 0 obj <<
- /Length 486
->>
-stream
-BT
-70 700 Td
-/F1 18 Tf
-(Link Annotations - Page 1) Tj
-0 -65 Td
-/F2 14 Tf
-(1. Link with destination to first page) Tj
-10 -20 Td
-/F2 14 Tf
-(2. Link with destination to second page) Tj
--12 -84 Td
-/F2 10 Tf
-(PDF Reference, Version 1.7, Section 8.4.5 defines Annotations) Tj
-2 -53 Td
-(3. An example of Highlight with text notes) Tj
-0 -18 Td
-(https://pdfium.googlesource.com/pdfium is link in plain text, not link annotation. These are referred to) Tj
-0 -17 Td
-(as WebLinks in PDFium.)Tj
-ET
-endstream
-endobj
-6 0 obj <<
- /Length 185
->>
-stream
-BT
-70 700 Td
-/F1 18 Tf
-(Link Annotations - Page 2) Tj
-0 -65 Td
-/F2 14 Tf
-(1. Link with destination to first page) Tj
-10 -20 Td
-/F2 14 Tf
-(2. Link with destination to second page) Tj
-ET
-endstream
-endobj
-7 0 obj <<
- /Type /Font
- /Subtype /Type1
- /BaseFont /Times-Roman
->>
-endobj
-8 0 obj <<
- /Type /Font
- /Subtype /Type1
- /BaseFont /Helvetica
->>
-endobj
-9 0 obj <<
- /Type /XObject
- /Subtype /Form
- /FormType 1
- /Length 18
- /BBox [293 530 349 542]
- /Resources <<
- /XObject <<
- /Form0 10 0 R
- >>
- /ExtGState <<
- /GS0 24 0 R
- >>
- >>
->>
-stream
-/GS0 gs
-/Form0 Do
-endstream
-endobj
-10 0 obj <<
- /Type /XObject
- /Subtype /Form
- /FormType 1
- /Group <<
- /S /Transparency
- >>
- /Length 59
- /BBox [293 530 349 542]
->>
-stream
-1.0 1.0 0.0 rg
-293 530 m
-349 530 l
-349 542 l
-293 542 l
-h f
-endstream
-endobj
-11 0 obj <<
- /Type /XObject
- /Subtype /Form
- /FormType 1
- /Length 18
- /BBox [83 440 178 453]
- /Resources <<
- /XObject <<
- /Form0 12 0 R
- >>
- /ExtGState <<
- /GS0 24 0 R
- >>
- >>
->>
-stream
-/GS0 gs
-/Form0 Do
-endstream
-endobj
-12 0 obj <<
- /Type /XObject
- /Subtype /Form
- /FormType 1
- /Group <<
- /S /Transparency
- >>
- /Length 57
- /BBox [83 440 178 453]
->>
-stream
-0.0 1.0 1.0 rg
-83 440 m
-178 440 l
-178 453 l
-83 453 l
-h f
-endstream
-endobj
-13 0 obj <<
- /Type /XObject
- /Subtype /Form
- /FormType 1
- /Length 18
- /BBox [149 476 191 487]
- /Resources <<
- /XObject <<
- /Form0 14 0 R
- >>
- /ExtGState <<
- /GS0 24 0 R
- >>
- >>
->>
-stream
-/GS0 gs
-/Form0 Do
-endstream
-endobj
-14 0 obj <<
- /Type /XObject
- /Subtype /Form
- /FormType 1
- /Group <<
- /S /Transparency
- >>
- /Length 59
- /BBox [149 476 191 487]
->>
-stream
-0.0 1.0 0.0 rg
-149 476 m
-191 476 l
-191 487 l
-149 487 l
-h f
-endstream
-endobj
-15 0 obj <<
- /Type /Annot
- /Subtype /Link
- /BS <<
- /W 0
- >>
- /Rect [69 633 542 653]
- /Dest [3 0 R /XYZ 200 725 0]
- /F 4
->>
-endobj
-16 0 obj <<
- /Type /Annot
- /Subtype /Link
- /BS <<
- /W 0
- >>
- /Rect [80 613 542 633]
- /Dest [4 0 R /XYZ 200 725 0]
- /F 4
->>
-endobj
-17 0 obj <<
- /Type /Annot
- /Subtype /Link
- /BS <<
- /W 0
- >>
- /Rect [66 529 196 544]
- /A <<
- /Type /Action
- /URI (https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/pdf_reference_1-7.pdf)
- /S /URI
- >>
- /F 4
->>
-endobj
-18 0 obj <<
- /Type /Annot
- /Subtype /Link
- /BS <<
- /W 0
- >>
- /Rect [83 440 178 453]
- /QuadPoints [83 453 178 453 83 440 178 440]
- /A <<
- /Type /Action
- /URI (https://cs.chromium.org/chromium/src/third_party/pdfium/public/fpdf_text.h)
- /S /URI
- >>
- /F 4
->>
-endobj
-19 0 obj <<
- /Type /Annot
- /Subtype /Highlight
- /AP <<
- /N 9 0 R
- >>
- /NM (Highlight-1)
- /F 4
- /QuadPoints [293 542 349 542 293 530 349 530]
- /P 3 0 R
- /C [1 0.90196 0]
- /Rect [293 530 349 542]
->>
-endobj
-20 0 obj <<
- /Type /Annot
- /Subtype /Highlight
- /AP <<
- /N 11 0 R
- >>
- /NM (Highlight-2)
- /F 4
- /QuadPoints [83 453 178 453 83 440 178 440]
- /P 3 0 R
- /C [0.26667 0.78431 0.96078]
- /Rect [83 440 178 453]
->>
-endobj
-21 0 obj <<
- /Type /Annot
- /Subtype /Popup
- /Parent 22 0 R
- /Rect [191 377 443 488]
->>
-endobj
-22 0 obj <<
- /Type /Annot
- /Subtype /Highlight
- /Popup 21 0 R
- /AP <<
- /N 13 0 R
- >>
- /NM (Highlight-With-Popup-1)
- /Contents (Text Note)
- /QuadPoints [149 487 191 487 149 476 191 476]
- /P 3 0 R
- /C [0.14902 0.90196 0]
- /Rect [149 476 191 487]
- /F 4
->>
-endobj
-23 0 obj <<
- /ca 1
- /Type /ExtGState
- /CA 1
- /BM /Normal
->>
-endobj
-24 0 obj <<
- /ca 1
- /Type /ExtGState
- /CA 1
- /AIS false
- /BM /Multiply
->>
-endobj
-xref
-0 25
-0000000000 65535 f
-0000000015 00000 n
-0000000068 00000 n
-0000000338 00000 n
-0000000475 00000 n
-0000000570 00000 n
-0000001108 00000 n
-0000001345 00000 n
-0000001423 00000 n
-0000001499 00000 n
-0000001749 00000 n
-0000001972 00000 n
-0000002222 00000 n
-0000002442 00000 n
-0000002693 00000 n
-0000002916 00000 n
-0000003056 00000 n
-0000003196 00000 n
-0000003443 00000 n
-0000003727 00000 n
-0000003944 00000 n
-0000004171 00000 n
-0000004269 00000 n
-0000004544 00000 n
-0000004615 00000 n
-trailer <<
- /Root 1 0 R
- /Size 25
->>
-startxref
-4701
-%%EOF
diff --git a/testing/resources/listbox_form.in b/testing/resources/listbox_form.in
index 354d841..054f21b 100644
--- a/testing/resources/listbox_form.in
+++ b/testing/resources/listbox_form.in
@@ -3,7 +3,7 @@
/Type /Catalog
/Pages 2 0 R
/AcroForm <<
- /Fields [ 8 0 R 9 0 R 10 0 R 11 0 R 12 0 R ]
+ /Fields [8 0 R 9 0 R 10 0 R 11 0 R 12 0 R 13 0 R 14 0 R]
/DR 4 0 R
>>
>>
@@ -11,16 +11,16 @@
{{object 2 0}} <<
/Type /Pages
/Count 1
- /Kids [ 3 0 R ]
+ /Kids [3 0 R]
>>
endobj
{{object 3 0}} <<
/Type /Page
/Parent 2 0 R
/Resources 4 0 R
- /MediaBox [ 0 0 300 600 ]
+ /MediaBox [0 0 300 600]
/Contents 7 0 R
- /Annots [ 8 0 R 9 0 R 10 0 R 11 0 R 12 0 R ]
+ /Annots [8 0 R 9 0 R 10 0 R 11 0 R 12 0 R 13 0 R 14 0 R]
>>
endobj
{{object 4 0}} <<
@@ -56,7 +56,7 @@
/Ff 0
/T (Listbox_SingleSelect)
/DA (0 0 0 rg /F1 12 Tf)
- /Rect [ 100 350 200 380 ]
+ /Rect [100 350 200 380]
/Opt [[(foo) (Foo)] [(bar) (Bar)] [(qux) (Qux)]]
>>
endobj
@@ -67,7 +67,7 @@
/Ff 2097152
/T (Listbox_MultiSelect)
/DA (0 0 0 rg /F1 12 Tf)
- /Rect [ 100 400 200 430 ]
+ /Rect [100 400 200 430]
/Opt [(Apple) (Banana) (Cherry) (Date) (Elderberry) (Fig) (Guava) (Honeydew)
(Indian Fig) (Jackfruit) (Kiwi) (Lemon) (Mango) (Nectarine) (Orange)
(Persimmon) (Quince) (Raspberry) (Strawberry) (Tamarind) (Ugli Fruit)
@@ -82,7 +82,7 @@
/Ff 1
/T (Listbox_ReadOnly)
/DA (0 0 0 rg /F1 12 Tf)
- /Rect [ 100 500 200 530 ]
+ /Rect [100 500 200 530]
/Opt [(Dog) (Elephant) (Frog)]
>>
endobj
@@ -91,24 +91,49 @@
/Subtype /Widget
/FT /Ch
/Ff 2097152
- /T (Listbox_MultiSelectMultipleSelected)
+ /T (Listbox_MultiSelectMultipleIndices)
/DA (0 0 0 rg /F1 12 Tf)
- /Rect [ 100 200 200 230 ]
- /Opt [(Alpha) (Beta) (Gamma) (Delta) (Epsilon)]
- /V [(Epsilon) (Gamma)]
+ /Rect [100 250 200 280]
+ /Opt [(Albania) (Belgium) (Croatia) (Denmark) (Estonia)]
+ /I [1 3]
>>
endobj
{{object 12 0}} <<
/Type /Annot
/Subtype /Widget
/FT /Ch
+ /Ff 2097152
+ /T (Listbox_MultiSelectMultipleValues)
+ /DA (0 0 0 rg /F1 12 Tf)
+ /Rect [100 200 200 230]
+ /Opt [(Alpha) (Beta) (Gamma) (Delta) (Epsilon)]
+ /V [(Epsilon) (Gamma)]
+>>
+endobj
+{{object 13 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Ch
+ /Ff 2097152
+ /T (Listbox_MultiSelectMultipleMismatch)
+ /DA (0 0 0 rg /F1 12 Tf)
+ /Rect [100 150 200 180]
+ /Opt [(Alligator) (Bear) (Cougar) (Deer) (Echidna)]
+ /V [(Alligator) (Cougar)]
+ /I [1 3 4]
+>>
+endobj
+{{object 14 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Ch
/Ff 0
/T (Listbox_SingleSelectLastSelected)
/DA (0 0 0 rg /F1 12 Tf)
- /Rect [ 100 100 200 130 ]
+ /Rect [100 100 200 130]
/Opt [(Alberta) (British Columbia) (Manitoba) (New Brunswick)
(Newfoundland and Labrador) (Nova Scotia) (Ontario)
- (Prince Edward Island) (Quebec) (Saskatchewan) ]
+ (Prince Edward Island) (Quebec) (Saskatchewan)]
/V (Saskatchewan)
/TI 9
>>
diff --git a/testing/resources/listbox_form.pdf b/testing/resources/listbox_form.pdf
index 9e1393e..10c5702 100644
--- a/testing/resources/listbox_form.pdf
+++ b/testing/resources/listbox_form.pdf
@@ -4,7 +4,7 @@
/Type /Catalog
/Pages 2 0 R
/AcroForm <<
- /Fields [ 8 0 R 9 0 R 10 0 R 11 0 R 12 0 R ]
+ /Fields [8 0 R 9 0 R 10 0 R 11 0 R 12 0 R 13 0 R 14 0 R]
/DR 4 0 R
>>
>>
@@ -12,16 +12,16 @@
2 0 obj <<
/Type /Pages
/Count 1
- /Kids [ 3 0 R ]
+ /Kids [3 0 R]
>>
endobj
3 0 obj <<
/Type /Page
/Parent 2 0 R
/Resources 4 0 R
- /MediaBox [ 0 0 300 600 ]
+ /MediaBox [0 0 300 600]
/Contents 7 0 R
- /Annots [ 8 0 R 9 0 R 10 0 R 11 0 R 12 0 R ]
+ /Annots [8 0 R 9 0 R 10 0 R 11 0 R 12 0 R 13 0 R 14 0 R]
>>
endobj
4 0 obj <<
@@ -57,7 +57,7 @@
/Ff 0
/T (Listbox_SingleSelect)
/DA (0 0 0 rg /F1 12 Tf)
- /Rect [ 100 350 200 380 ]
+ /Rect [100 350 200 380]
/Opt [[(foo) (Foo)] [(bar) (Bar)] [(qux) (Qux)]]
>>
endobj
@@ -68,7 +68,7 @@
/Ff 2097152
/T (Listbox_MultiSelect)
/DA (0 0 0 rg /F1 12 Tf)
- /Rect [ 100 400 200 430 ]
+ /Rect [100 400 200 430]
/Opt [(Apple) (Banana) (Cherry) (Date) (Elderberry) (Fig) (Guava) (Honeydew)
(Indian Fig) (Jackfruit) (Kiwi) (Lemon) (Mango) (Nectarine) (Orange)
(Persimmon) (Quince) (Raspberry) (Strawberry) (Tamarind) (Ugli Fruit)
@@ -83,7 +83,7 @@
/Ff 1
/T (Listbox_ReadOnly)
/DA (0 0 0 rg /F1 12 Tf)
- /Rect [ 100 500 200 530 ]
+ /Rect [100 500 200 530]
/Opt [(Dog) (Elephant) (Frog)]
>>
endobj
@@ -92,47 +92,74 @@
/Subtype /Widget
/FT /Ch
/Ff 2097152
- /T (Listbox_MultiSelectMultipleSelected)
+ /T (Listbox_MultiSelectMultipleIndices)
/DA (0 0 0 rg /F1 12 Tf)
- /Rect [ 100 200 200 230 ]
- /Opt [(Alpha) (Beta) (Gamma) (Delta) (Epsilon)]
- /V [(Epsilon) (Gamma)]
+ /Rect [100 250 200 280]
+ /Opt [(Albania) (Belgium) (Croatia) (Denmark) (Estonia)]
+ /I [1 3]
>>
endobj
12 0 obj <<
/Type /Annot
/Subtype /Widget
/FT /Ch
+ /Ff 2097152
+ /T (Listbox_MultiSelectMultipleValues)
+ /DA (0 0 0 rg /F1 12 Tf)
+ /Rect [100 200 200 230]
+ /Opt [(Alpha) (Beta) (Gamma) (Delta) (Epsilon)]
+ /V [(Epsilon) (Gamma)]
+>>
+endobj
+13 0 obj <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Ch
+ /Ff 2097152
+ /T (Listbox_MultiSelectMultipleMismatch)
+ /DA (0 0 0 rg /F1 12 Tf)
+ /Rect [100 150 200 180]
+ /Opt [(Alligator) (Bear) (Cougar) (Deer) (Echidna)]
+ /V [(Alligator) (Cougar)]
+ /I [1 3 4]
+>>
+endobj
+14 0 obj <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Ch
/Ff 0
/T (Listbox_SingleSelectLastSelected)
/DA (0 0 0 rg /F1 12 Tf)
- /Rect [ 100 100 200 130 ]
+ /Rect [100 100 200 130]
/Opt [(Alberta) (British Columbia) (Manitoba) (New Brunswick)
(Newfoundland and Labrador) (Nova Scotia) (Ontario)
- (Prince Edward Island) (Quebec) (Saskatchewan) ]
+ (Prince Edward Island) (Quebec) (Saskatchewan)]
/V (Saskatchewan)
/TI 9
>>
endobj
xref
-0 13
+0 15
0000000000 65535 f
0000000015 00000 n
-0000000151 00000 n
-0000000216 00000 n
-0000000379 00000 n
-0000000414 00000 n
-0000000447 00000 n
-0000000523 00000 n
-0000000625 00000 n
-0000000832 00000 n
-0000001302 00000 n
-0000001488 00000 n
-0000001741 00000 n
+0000000163 00000 n
+0000000226 00000 n
+0000000399 00000 n
+0000000434 00000 n
+0000000467 00000 n
+0000000543 00000 n
+0000000645 00000 n
+0000000850 00000 n
+0000001318 00000 n
+0000001502 00000 n
+0000001747 00000 n
+0000001996 00000 n
+0000002267 00000 n
trailer <<
/Root 1 0 R
- /Size 13
+ /Size 15
>>
startxref
-2119
+2642
%%EOF
diff --git a/testing/resources/marked_content_id.in b/testing/resources/marked_content_id.in
new file mode 100644
index 0000000..ebc217f
--- /dev/null
+++ b/testing/resources/marked_content_id.in
@@ -0,0 +1,235 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /MarkInfo <<
+ /Marked true
+ >>
+ /Pages 2 0 R
+ /StructTreeRoot 11 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Contents 4 0 R
+ /MediaBox [0 0 612 792]
+ /Parent 2 0 R
+ /Resources <<
+ /ProcSet [/PDF /Text /ImageC]
+ /ColorSpace <<
+ /CS0 /DeviceRGB
+ >>
+ /Font <<
+ /TT0 5 0 R
+ >>
+ /XObject <<
+ /Im0 9 0 R
+ >>
+ >>
+ /StructParents 0
+ /Tabs /S
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+/Artifact <<
+ /Type /Layout
+ /BBox [408 516 411 522 ]
+>>
+BDC
+q
+18 40 576 734 re
+W n
+BT
+/CS0 cs 0 0 0 scn
+/TT0 1 Tf
+3 Tr 12 0 0 12 408.3599 516 Tm
+(!)Tj
+ET
+EMC
+/Figure <<
+ /MCID 0
+>>
+BDC
+/ClipSpan
+BMC
+Q
+q
+/Clip
+BMC
+203.52 516 204.96 204 re
+W n
+EMC
+q
+/Perceptual ri
+204.96 0 0 204 203.52 516 cm
+/Im0 Do
+Q
+EMC
+EMC
+Q
+endstream
+endobj
+{{object 5 0}} <<
+ /Type /Font
+ /Subtype /TrueType
+ /BaseFont /KPRGOG+Calibri
+ /FirstChar 33
+ /FontDescriptor 6 0 R
+ /LastChar 33
+ /ToUnicode 8 0 R
+ /Widths [226]
+>>
+endobj
+{{object 6 0}} <<
+ /Type /FontDescriptor
+ /Ascent 952
+ /AvgWidth 521
+ /CapHeight 644
+ /Descent -269
+ /Flags 4
+ /FontBBox [-503 -307 1240 1026]
+ /FontFile2 7 0 R
+ /FontName /KPRGOG+Calibri
+ /ItalicAngle 0
+ /MaxWidth 1328
+ /StemV 0
+ /XHeight 476
+>>
+endobj
+{{object 7 0}} <<
+ /Filter [/ASCII85Decode /FlateDecode]
+ {{streamlen}}
+>>
+stream
+GhOYs.SXL4?t`qUAme0T9e9ZP"3(Hd+dJWtR0Mc^!/VL0WJN&85/SHb2Tl(-5ik^jKM"^O8Hk>#!@0XF
+nrmMQ2*caF2mEF!5/SB`<ldqX9ndPSE^5#N*5h&A+<]SBUVVNeJe#T[$]OJp46d]&Jj98Q)'RR_N+Q$s
+&7@KA^s9^uV"GVZTScj%Z64ENVLB-%'.NJL2I@=I3P\P)?e:eKd2..*\c;[%\!usuLDc2s#rI26AhNtn
+^UG$s\/,#AokOTk)[8bg-SVgp!L942WeSUc0X(R4"+qe&KRog"#T4I])&W>f1LFVQ"3(C(K-c.'K-*pZ
+TV<PGMsrI+Z\u%:/*SdVWiMO*)]RHbR@:''/+G?F`D"M"m?ilmjHu<'?m9bf+?h%AV*@i$rdWUglBL\R
+N*58<pdKG\=IE\FKQM'1"bNLLD=hAlRG$8\=ZP+lbak!2F'OuJYm\^3MTH3FE!XRCM9612s1Q\"pZiJC
+hf!.nSDR84frS]M<L@E<EA4Z%Z0dX:OOUR:oF0ar(1?):((_2??l'I:d;Xs<gEnBP1.+N1'OoX#?V"9)
+62f*K!":%>J09&cGAZN]SBRe'9J-<tdVIk~>
+endstream
+endobj
+{{object 8 0}} <<
+ {{streamlen}}
+>>
+stream
+/CIDInit /ProcSet findresource begin
+12 dict begin
+begincmap
+/CIDSystemInfo <<
+ /Registry (Adobe)
+ /Ordering (UCS)
+ /Supplement 0
+>> def
+/CMapName /Adobe-Identity-UCS def
+/CMapType 2 def
+1 begincodespacerange
+<00><FF>
+endcodespacerange
+1 beginbfrange
+<21><21><0009>
+endbfrange
+endcmap
+CMapName currentdict /CMap defineresource pop
+end
+end
+endstream
+endobj
+{{object 9 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 8
+ /ColorSpace /DeviceRGB
+ /Filter [/ASCII85Decode /FlateDecode]
+ /Height 272
+ /Intent /Perceptual
+ /Interpolate true
+ /SMask 10 0 R
+ /Width 273
+ {{streamlen}}
+>>
+stream
+GhVQ=BrkT/*<>Tnp)[K];NNO';(,Y,e3[reZBQQ?W!Kf<[A?;p.U_Il/M348;ME%+5X5%[`$K.l1b(tG
+TLMZ2+XWWp'13=j`\iPk)E1o?VW[ZsH+/!EYO=ZTq+Z;[m[Z0.gc9$0IqH7cL&Ks6zzzzzzzzzzzzzzz
+zzzzzzzzzzzi5kWPD5!Dac`&SeQ0\"gG?d2ed.;V%H`m^)\?kGADd=@:VD4jXEa<PJG,W`gbI)ZrQ!rE
+mq<cX8_p>e&W:iIMjU]Ac;PpMX%H8<GhV0ff3+WZ!HIIKD_2kcp(LEsJoBS&([m(jN>?9X4Isl\@B)VH
+ei?PN:-bUd:es1a46`.c4#oSK[[&N_Hpg#E(2et8dNjOo4Y\o6mC?8&0=r[0BSDQNBl[LHrrinESN2aM
+H]X[shFSd6Yp6u;/h'+G@dCrba%Fh+VIeW!SI"c<255j-(s+rVNHr1p8m8/;H/)Q#PhA/;iZ1`M;dHTj
+,*\s38\+d-:.HfJ"C2Z>LZMr;Er#sg8DXB?B*?@&/POJ=2Rp.acH1S'c:0h%Qf1/p(_WUYP..Hoq_lC7
+UqW'>J9UIja/=74H/IfJ0UNSK>[EA-iM;_7B;s#=jF65n=VC>ZS])C]u!<LrVP)Et-hQ^Ki?[<aU2WMi
+S[rGk5[#g-0I/C3k+d75h;$Ti8O+(Nt:dX;_GD?E)V!:\B9uBJN?e@>&4d!kgpFji7r5#KakOp4$nkmf
+'eOsCc@\^uX=/66#_jB5In+iQ57QP'hMiSe,i3Xk*8\MecZ[,s'^[&c+h!T9k85jhM&'r-%R25'SVHGB
+9k#XV_9R?#BDhfJLT,ZreD`jrr^qk`H-eIGW^[0@]]-7aBA\.>nnFsQ4`1G];\9@kt4\_Dgq4'8WPj-$
+u_lHNB^YCR9OEk;6NgF<)V&<.s$'T[U:/UA%GD$h=^Ge@VWtFutQ*iBll8J2VF878mS2e6qqssAI](5J
+ultb2`,%T8rR55Z(#hPT3c<3mQMD:rt@imcuhsQg'BB=mCc:obN[AW,VDr*/VCRGZ"VEEVs;/)_0AF;K
+CQS/T404*C@0Nr`kqmB+F&\k[a]3Mg4VAc.Jp05hk`Z?*&@jO2FR5eB8Qb7E`RXE\'mVP$=jZKk*a-=<
+o%<4Nj@9i1H+#gG_VS)(L\KPin1iabgE7Rl\`Hnt3e%u-@inXMl4,7JE5H'\>oG^]C/R82DqN.[/Z5TT
+Abj7/s5$+-\4KuJ!k$S"+oTc3@p]Cu:HCjQ\("LiV_\<pFcgMmJOGPL:Eg0]dUOb12i[T3@EV3=[Gl<O
+319HoCeUAPU>_cY5<G-Cl1?HM]9Fn;I+]GPt/j6b2q=os6KKn7dH[!G?F5"qpMQYU9=f^:m*H#&h#at!
+.Sc!,=c/W3>>8DB5aX)9EcKD*"9$<a19Z+H\fSQr'66$]N*AZ0%faVTGMT):eO*;([PPH$Lo>Ia.Dap7
+iQ7:[SR.Kt$nE;_eKDNmrSVYbTnei/am)MoEVY'#Z=EA*'B!![Bi\U1Xrq<r\/OJT(2s*J#Au5LrZ)c>
+-(V<:Ebf4dId6D-GdA-8uEa81%C#-u'fc&FYfU)VT2jqjEd^V#G65A,L)s5aq"(iVq5.b\DbPtR`F6:H
+J_p+*[1W>Oo_ofZZj>'MId`I8I*8epRQi,B$O&pXGCu"2s2f@43DHi6pg+r;^M=rN<4OWmC'u"Zq,9h=
+.;lAdFZM77U$fM9<Elh+ZD,n'-&",C^cCE>XU>iMX=?LaD1=,@qo8dg/Ses-NXqkT%9NRcJ^$.bA@T-;
+dBg6iBe%]J62X9a(XZ!F3XY7EgQG`YM1Hp%jI;53^`H0$jf:6@!PibJ?l[O&ZY(FDnq(4%U8'gO<>.9V
+)RHV,l_Y.'.$;Bk^_tl+Je#)m:?Iroo^Y)DNJ)(dpH!)%Bit]Q!Ramb%Qf#Dd5][2Kzzzzzzzzzzzzzz
+zzzzzzzzzz!!!#Ws20Ni!j00"QN~>
+endstream
+endobj
+{{object 10 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 8
+ /ColorSpace /DeviceGray
+ /Filter [/ASCII85Decode /FlateDecode]
+ /Height 272
+ /Interpolate true
+ /Width 273
+ {{streamlen}}
+>>
+stream
+GhVQ$0b"*_!!8hNrdD20"'P[)zzzzzzzzzzzzzzzzz!8pQUgs,iQ~>
+endstream
+endobj
+{{object 11 0}} <<
+ /Type /StructTreeRoot
+ /K 12 0 R
+ /ParentTree <<
+ /Nums [0 13 0 R]
+ >>
+ /ParentTreeNextKey 1
+>>
+endobj
+{{object 12 0}} <<
+ /S /Figure
+ /P 11 0 R
+ /A 14 0 R
+ /Alt (Hello!\000)
+ /K 0
+ /Pg 3 0 R
+>>
+endobj
+{{object 13 0}}
+ [12 0 R]
+endobj
+{{object 14 0}} <<
+ /BBox [204 516 408 720]
+ /InlineAlign /Center
+ /O /Layout
+ /Placement /Block
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/marked_content_id.pdf b/testing/resources/marked_content_id.pdf
index ddbf11f..cf80171 100644
--- a/testing/resources/marked_content_id.pdf
+++ b/testing/resources/marked_content_id.pdf
Binary files differ
diff --git a/testing/resources/matte.in b/testing/resources/matte.in
new file mode 100644
index 0000000..57d8e13
--- /dev/null
+++ b/testing/resources/matte.in
@@ -0,0 +1,172 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 100 150]
+ /Contents [4 0 R]
+ /Resources <<
+ /XObject <<
+ /Mask 5 0 R
+ /NoMask 7 0 R
+ /MaskTooFewComps 9 0 R
+ /MaskTooManyComps 11 0 R
+ >>
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+40 0 0 60 0 0 cm
+/Mask Do
+Q
+q
+40 0 0 60 50 0 cm
+/NoMask Do
+Q
+q
+40 0 0 60 0 90 cm
+/MaskTooFewComps Do
+Q
+q
+40 0 0 60 50 90 cm
+/MaskTooManyComps Do
+Q
+endstream
+endobj
+{{object 5 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /Width 50
+ /Height 50
+ /BitsPerComponent 8
+ /ColorSpace /DeviceRGB
+ /Filter [/ASCIIHexDecode /FlateDecode]
+ /SMask 6 0 R
+ {{streamlen}}
+>>
+stream
+789cedc2310d00000c03a07f2aaab3ea7bcf03842655555555555555f5bf01cc7818dc
+endstream
+endobj
+{{object 6 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /Width 50
+ /Height 50
+ /BitsPerComponent 8
+ /ColorSpace /DeviceGray
+ /Filter [/ASCIIHexDecode /FlateDecode]
+ /Matte [0.0 0.2 1]
+ {{streamlen}}
+>>
+stream
+789cabaa1a05a3808e4061148c027a028351300ae809a246c128a027e0020065c9c90d
+endstream
+endobj
+{{object 7 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /Width 50
+ /Height 50
+ /BitsPerComponent 8
+ /ColorSpace /DeviceRGB
+ /Filter [/ASCIIHexDecode /FlateDecode]
+ /SMask 8 0 R
+ {{streamlen}}
+>>
+stream
+789cedc2310d00000c03a07f2aaab3ea7bcf03842655555555555555f5bf01cc7818dc
+endstream
+endobj
+{{object 8 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /Width 50
+ /Height 50
+ /BitsPerComponent 8
+ /ColorSpace /DeviceGray
+ /Filter [/ASCIIHexDecode /FlateDecode]
+ {{streamlen}}
+>>
+stream
+789cabaa1a05a3808e4061148c027a028351300ae809a246c128a027e0020065c9c90d
+endstream
+endobj
+{{object 9 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /Width 50
+ /Height 50
+ /BitsPerComponent 8
+ /ColorSpace /DeviceRGB
+ /Filter [/ASCIIHexDecode /FlateDecode]
+ /SMask 10 0 R
+ {{streamlen}}
+>>
+stream
+789cedc2310d00000c03a07f2aaab3ea7bcf03842655555555555555f5bf01cc7818dc
+endstream
+endobj
+{{object 10 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /Width 50
+ /Height 50
+ /BitsPerComponent 8
+ /ColorSpace /DeviceGray
+ /Filter [/ASCIIHexDecode /FlateDecode]
+ /Matte [0.0 0.2]
+ {{streamlen}}
+>>
+stream
+789cabaa1a05a3808e4061148c027a028351300ae809a246c128a027e0020065c9c90d
+endstream
+endobj
+{{object 11 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /Width 50
+ /Height 50
+ /BitsPerComponent 8
+ /ColorSpace /DeviceRGB
+ /Filter [/ASCIIHexDecode /FlateDecode]
+ /SMask 12 0 R
+ {{streamlen}}
+>>
+stream
+789cedc2310d00000c03a07f2aaab3ea7bcf03842655555555555555f5bf01cc7818dc
+endstream
+endobj
+{{object 12 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /Width 50
+ /Height 50
+ /BitsPerComponent 8
+ /ColorSpace /DeviceGray
+ /Filter [/ASCIIHexDecode /FlateDecode]
+ /Matte [0.0 0.2 1 0.5]
+ {{streamlen}}
+>>
+stream
+789cabaa1a05a3808e4061148c027a028351300ae809a246c128a027e0020065c9c90d
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/matte.pdf b/testing/resources/matte.pdf
new file mode 100644
index 0000000..05d1ecd
--- /dev/null
+++ b/testing/resources/matte.pdf
@@ -0,0 +1,191 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 100 150]
+ /Contents [4 0 R]
+ /Resources <<
+ /XObject <<
+ /Mask 5 0 R
+ /NoMask 7 0 R
+ /MaskTooFewComps 9 0 R
+ /MaskTooManyComps 11 0 R
+ >>
+ >>
+>>
+endobj
+4 0 obj <<
+ /Length 149
+>>
+stream
+q
+40 0 0 60 0 0 cm
+/Mask Do
+Q
+q
+40 0 0 60 50 0 cm
+/NoMask Do
+Q
+q
+40 0 0 60 0 90 cm
+/MaskTooFewComps Do
+Q
+q
+40 0 0 60 50 90 cm
+/MaskTooManyComps Do
+Q
+endstream
+endobj
+5 0 obj <<
+ /Type /XObject
+ /Subtype /Image
+ /Width 50
+ /Height 50
+ /BitsPerComponent 8
+ /ColorSpace /DeviceRGB
+ /Filter [/ASCIIHexDecode /FlateDecode]
+ /SMask 6 0 R
+ /Length 71
+>>
+stream
+789cedc2310d00000c03a07f2aaab3ea7bcf03842655555555555555f5bf01cc7818dc
+endstream
+endobj
+6 0 obj <<
+ /Type /XObject
+ /Subtype /Image
+ /Width 50
+ /Height 50
+ /BitsPerComponent 8
+ /ColorSpace /DeviceGray
+ /Filter [/ASCIIHexDecode /FlateDecode]
+ /Matte [0.0 0.2 1]
+ /Length 71
+>>
+stream
+789cabaa1a05a3808e4061148c027a028351300ae809a246c128a027e0020065c9c90d
+endstream
+endobj
+7 0 obj <<
+ /Type /XObject
+ /Subtype /Image
+ /Width 50
+ /Height 50
+ /BitsPerComponent 8
+ /ColorSpace /DeviceRGB
+ /Filter [/ASCIIHexDecode /FlateDecode]
+ /SMask 8 0 R
+ /Length 71
+>>
+stream
+789cedc2310d00000c03a07f2aaab3ea7bcf03842655555555555555f5bf01cc7818dc
+endstream
+endobj
+8 0 obj <<
+ /Type /XObject
+ /Subtype /Image
+ /Width 50
+ /Height 50
+ /BitsPerComponent 8
+ /ColorSpace /DeviceGray
+ /Filter [/ASCIIHexDecode /FlateDecode]
+ /Length 71
+>>
+stream
+789cabaa1a05a3808e4061148c027a028351300ae809a246c128a027e0020065c9c90d
+endstream
+endobj
+9 0 obj <<
+ /Type /XObject
+ /Subtype /Image
+ /Width 50
+ /Height 50
+ /BitsPerComponent 8
+ /ColorSpace /DeviceRGB
+ /Filter [/ASCIIHexDecode /FlateDecode]
+ /SMask 10 0 R
+ /Length 71
+>>
+stream
+789cedc2310d00000c03a07f2aaab3ea7bcf03842655555555555555f5bf01cc7818dc
+endstream
+endobj
+10 0 obj <<
+ /Type /XObject
+ /Subtype /Image
+ /Width 50
+ /Height 50
+ /BitsPerComponent 8
+ /ColorSpace /DeviceGray
+ /Filter [/ASCIIHexDecode /FlateDecode]
+ /Matte [0.0 0.2]
+ /Length 71
+>>
+stream
+789cabaa1a05a3808e4061148c027a028351300ae809a246c128a027e0020065c9c90d
+endstream
+endobj
+11 0 obj <<
+ /Type /XObject
+ /Subtype /Image
+ /Width 50
+ /Height 50
+ /BitsPerComponent 8
+ /ColorSpace /DeviceRGB
+ /Filter [/ASCIIHexDecode /FlateDecode]
+ /SMask 12 0 R
+ /Length 71
+>>
+stream
+789cedc2310d00000c03a07f2aaab3ea7bcf03842655555555555555f5bf01cc7818dc
+endstream
+endobj
+12 0 obj <<
+ /Type /XObject
+ /Subtype /Image
+ /Width 50
+ /Height 50
+ /BitsPerComponent 8
+ /ColorSpace /DeviceGray
+ /Filter [/ASCIIHexDecode /FlateDecode]
+ /Matte [0.0 0.2 1 0.5]
+ /Length 71
+>>
+stream
+789cabaa1a05a3808e4061148c027a028351300ae809a246c128a027e0020065c9c90d
+endstream
+endobj
+xref
+0 13
+0000000000 65535 f
+0000000015 00000 n
+0000000068 00000 n
+0000000131 00000 n
+0000000370 00000 n
+0000000571 00000 n
+0000000856 00000 n
+0000001148 00000 n
+0000001433 00000 n
+0000001704 00000 n
+0000001990 00000 n
+0000002281 00000 n
+0000002568 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 13
+>>
+startxref
+2865
+%%EOF
diff --git a/testing/resources/mona_lisa.fragment b/testing/resources/mona_lisa.fragment
new file mode 100644
index 0000000..873677d
--- /dev/null
+++ b/testing/resources/mona_lisa.fragment
@@ -0,0 +1,109 @@
+/9j/4AAQSkZJRgABAQEAZABkAAD//gBSRmlsZSBzb3VyY2U6IGh0dHA6Ly9jb21tb25zLndpa2lt
+ZWRpYS5vcmcvd2lraS9GaWxlOk1vbmFfTGlzYV9mYWNlXzgwMHg4MDBweC5qcGf/2wBDAAYEBQYF
+BAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUo
+KSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgo
+KCgoKCgoKCgoKCgoKCj/wAARCAB4AHgDAREAAhEBAxEB/8QAHAAAAgIDAQEAAAAAAAAAAAAABQYE
+BwEDCAIA/8QAOxAAAgECBQIEBAQFAwMFAAAAAQIDBBEABRIhMQZBEyJRYQcycYEUkaGxFUJiwfAj
+JOEWF9ElM1LC8f/EABoBAAIDAQEAAAAAAAAAAAAAAAIDAQQFAAb/xAAvEQACAgICAgAEBQQCAwAA
+AAAAAQIRAyESMQRBBRMiUTJhcaGxFCNSgZHwweHx/9oADAMBAAIRAxEAPwBqiIChT5t7gEYwX1ss
+JUbY0ErKBHpFu498Q3fRKTMmJVNm2POo9/tiG2kTFG+FfIAq9t2ta2BtvsKtkqKmDE2UgMDiUn2C
+3ugTnvVGRdPy6M2zKFJxv+HivLN6i6LuPvbDsalPaRPy29Cw/wAYOn1kPgUmYS3OxZUjJvwbFrgY
+fHx8hziqCVF8V+namVUnSvojzqlp9SkD3QkYGWKa62SsY5ZVmVDmlMKnLamCqgvu8bBgPYjkH64U
+3x/FpkcWF4yT8oG4337YBzdUgVHezEwN1N1BxzurslJGI1uSpJG3bvjlew2bYUN+fS+Cj12DJkqE
+WNgOeL4OM66AaNscXkb2vziVK47ZD0IkcG1r/X/PthVWFZLRNKqAAdiCT9cC19jk77MRpqILKNjf
+i/tbCpcug40SooAz6tNyfLsOPbBQhslsrH4n/ECXLa+Tp7IZvBrxaOprEXU0Fx8if1+/a/ri3i8e
+1zn1/P8A6Ba6oR8h6YqY8szKshpauaWdNCzBHkZifmYk778ffAZvIU5Qg3pMsQg4xbS2KmcZQKPU
+aqmqItVhqeEqL9rnGhjzc3SaZXyY63QGDvSuJaScofVfMp57YsOPLUkLUnHcQplXUFfQZrFV5bK2
+XZkll8SEWRx6MnBB9D+mEywppqW0N+by/U6Y+EvXdP1rlrJKkVNnVMAtVTLsrDgSJ30nj2O3pjLz
+4XhlXr0d+JWh6eJQCy2b6DEOKAT+55hQrYsNrW98TH2S2ToAATYG5GwI4wUqSYPZuSEBNXAIBO2E
+8eO0TZ8UZVtf5hvbE7RHYkQRG6kix7f2wuMnexjr0SvDZiqpf6dsH30D12bIKe77gna7W7bYHbbC
+tIFdf9R0/R3S9VmcrIJFHh06t/PKR5Rbk+v0GLMISk1CK2wE03begP8ACHommhy6LM8zjhqM0qf9
+eeoljEjhm3sL7D7b++KmXK8+Tin9K0l+hcUflwTrbLXFHCABftYdvtthbjRHNgjOsoiqKaaJ9Tqy
+kFWAKn88Vsip6HwlfZzV8R+kKeikeSjS128wCD+2NT4f57yfTP0I8jCltFWyJJEXul0vYkLce4Jx
+tqSfRRoJ9K5/WdNdSUOb5a7iWmbUVPEkZI1o3qCt/wBMBmxLLjcP+P1IjOpWztvJa+lzTJ6PMqF9
+dJVxLLG47qwuPpbi2MqP0Opd9ByV9E2KNWF97AbNiWlu2crN8cIABIFxyL7YW2yTZGhlNr7eltsc
+nZD0ZkpyI++1+cMcVVg3sUUWMxRtHpbVc3GKr4voYrM063k32YEi9t9zhiTXRDJVHAzSB3uoK2F/
+bEwm4u7OaT0UL8cs9TqHPsqyZEH+nWxaWvuus7KB6keYn6Dti5gySm5Zn0kyflrGlBdtl7dNWp6F
+FY2awU39RjFxx1aNHJ9g6pdWa5LX3J9MS27aE0uyHXiSRCFN9rG3OEThyGxaQi9UdOpXwyCW1muD
+b/PbC4J4ZckNdTVM5u63y5cpzeemilCQDzebuff9bY9R4eV5cak+zMzxUJUhVmbUNQt6W9bC++Lp
+WaSOjvgT1JK/w+ho5mtHl9Y1OD6o1pB9hrP5Yy/Mhxm697Gp3Wy0aXNhNMsIYi29u1j2v37Yq7fZ
+LetB01arE5DKVQaRY8m+/wDbHUxTnvQRplvbcaTuL+/GIXdMY2bwbxFd2wXNtUD07KMyvqid3WGn
+LvqszF0PIB8o/LfEairZNT9D109mYzSmMiRaJUkMbjmxIDCx+hwacpNEKadk3Nq38HlE8oGpvw7s
+thvcKdsLlG6TCUt0jkbrCsaXramzCGSNr10MmkfNrUqtxfsQBtjS8eP9hwf2f7jJ25KS+6Lv/wC6
+VBDlQq0lSipEm/DhqpZSddr2IRG08H5iDtxjGXi5XJQjt1fa6L7yQrkx1n6p0dKSZwIwAE4vca+B
+Y973B++KXJyfFd9B/LS2xLTq7NKbIWzvOfHpaeRxGFWkknILfKCFsBe3rg/lKWR48cr/ADbpHcmo
+3JAOLrjqHPcwio8uyeaohcj/AHUaSJGBv5jrUabW4P64tT8XHjjynk39tX+wr5km6jErP4oUM8Od
++JUfM/zC99/rjT+GZYvHSRV8lNSViSmmF2XUrM1wQefe2NTsp7uix/hJWFKDNaUMViM0E5AYgsNJ
+Ui/2GKHmR/C/ZPWi5MkzgykxMYlABszCxuP32xVUFsGWR+w1Lm0cNJHGAJRIw3U302bfbne2Ipds
+SOXT9UKqEsHLJqBH0HbFKVqSTLqaadByJ/D1FgbW2vhuO07BbXRydQZocvqqclJCW31XA3IsSe1z
+fbFiWHndC1krQfyLrl6TI5KKFNFTGDaULfU1yGJ/NQCe2AyYJJ/T0Hjkktk2l6tmoTmVI0zSrBEq
+wySG5RmZthc8AXIxPy3KCZy1Oijxoi67yiKUf7eHNI1Z73DDxRck+v8A4xou/wCnlXbi/wCBqf1x
+17OwP4TBW05RJqiJTJ4jBQu7X3O4Nj7jHmFBTjyTNNzcHRo6ioUk6ckprBYGlBFxfYf/AIMdwaWi
+LTeyblGXRnLYYWqaiOwsdDDf87jELFyVSOlNx/Caa5abKKdjCZGL3LNI5YnC5pQdRQULkrZyz8Yc
+wNZ1G3hFrIeB2/y2PRfCYccNv2Z/lu5UhEcuXErWYHdibbE/2xq1ZRHz4VpNP1BVU1I0i+PRlyq2
+a5V1P/2/XFPyopQTfoJLk9Fy5TRVkMjvLTVMpA3ZrHew/I/bvijyW9kSg0uiWIK2PSYaGpYA7Cwv
+xyT3xzURLvsaukq2opJSKiCWJGJtZCdz9sJlFdpjMc2tMbJMxQqbyMCb8jAPI1sclZyPmMBZ2ZZW
+GkeJqLaix4A+mNODr0IaNbMYiDDJIAbgMBbUpAO4F9r3/K+J2yaT7AGeVdTYtHIG1Pu3qTcbn7n8
+sWMUVQSb5C3mFayrGYXa8bhyrdiDdbHvxh0YJ6GznpUdk9I9QJmuUUciOC00aEXO1iAf748jNPHc
+H6dGy6nUgN8QOuafpqloqGty+tkr5m3iiFw68albhhxxv64b4+CedaaSX3ETlHG7e7GLpPMzU9OU
+lZVRS0krhv8AQlPnUajpv72thTSg2rug/wASTFzrTNZTCywgkr9sLjHnJX6Dk6WjmLrCoaozeWR7
+sdR2btvwceq8VVjSRlZvxAaWcNEpK2f+axPGLCEMNdLVMkWYRmJn8UkqpHuLf87emByxuLvoh7Lb
+ynM8wphrSSrjB8xW1lJ2/wA7YoNKnQDeg7H1DXooAmrFckWUbn9rdsQ8a9CqDtB1XUJvUzh1202s
+Dv625tgJwXVEptK7CY6jlqoisTygggGy7geuFcFdkcm/pbOdpa7xpQzCXzeQG2s27WONCMKHHppn
+lHhqg1KbhhtfjkG37euJBe2Q81y92y8vM6HUy2AI8p3P9t//ADgoZVy0iYJ3QqThI4NBVZJmc3bk
+qB6fXFlNt2Ntca9l5fA7qWlzTp6PJ6ipNPmVIDGrgjV4d/Kwvza9vtjznxXBKGR5K+mX8ml4mVTg
+oe0WR1OlR4UUZrcwqHUXSdcvjk29yLAfljN0pW/5LXoh5DA1NMZszrKyrn07LKiokYHoFHJuNyTi
+ZSjJrVV+oKX52LvXGcwDxI42BYgjbfe22H+Pi5y2LyTpUc/Z9KZcwZ2Yl2a/t7DHqMKSjSMvK9gy
+VlvZCRtp+vqcNQtumFMhDfjIWifS9wb3taxwvJJKLOirLlpljjdYiulnF/KfNuNhx9+cZnPTdC3H
+VBCSjUxqZFOlPMCU77knb2xHNN7YFM30ckEQIAXTfY6fMLd8Tr2CyfJnsUKlIULNfsPl55PpxiJQ
+V2FG0ikqKoLlRpRlbSCjC4O/Fr8k+98aEo/caHaWYyOVMOXBRIWCSHYj6ffCZxXabIoL5hl0/wD0
+5WySJQNEkQmURhiWCkG1jyQL++EJrmtjIqip8x8JpnnkRXZyQNPC27n12tjSiq0TrtkGOtlocziq
+sullp54SGjkDWa+3+Wwc4KUeM9pkW4SuLOi+kfjrlz9PxwZ0r0+ZIgWTSl0f+ofX0x5/P8Kywf8A
+a3H+DRx+VCf4tMEZ/wDEuOoB/A6UjI80kjC/5emBx/DZ95Dp511ErvMuojVvppr1EzXJfgL32vzj
+Sx+Lw70V5ZL62LVXSy+O5ZjIxNy9tgMXLVUV3FoinQiG+7WNiB++D7AJuWzGJ1K3BUcn7EYiULVE
+xdOy6qLNoTl1LIWEr2XSVIsGNr77d7jb9cZrwtXE7l7DdVXxVETeV4ybKoQ727m3bfCfkxUrBcrV
+UQBXwq7GJZmXRqKActf0+ww2Ka0C4Jn1TnETVI0sCC1iBGPU/p+3ptjpJvs5QS9i1Q0IVUctAlmu
+PCjB/ckg4a5xYwPUpiiZ1srPqHnbw9TahyABzta+Ikv8URFm+oqA0Eo8VDGV0MqkEDm4P14++EO0
+9DE12UbnlGtPn1VTh2aNJLIVTc+VSARtv5gMa2KVwUgX2BZUu6nURcXJY73w7sGSoO9ARUcnWuUQ
+5giyUk1QIXRvl84Ki/3IxW8vl8mTi6dB4GlkXItXqb4WU8JY0EcSWO5AuAMZGH4nKL4z2Wsnjp7R
+AynonTIgnjUpcCygj8v3wefzk1pnQxv2TOrOlBT5S/gRqiqoJ2tbfck+mEeN5vLJ9TGSxaKlqKfw
+igdGUgHy25B4xvRmn0UZRNog8GjSWQeQFjbttYYlTTlR0o0rLL6OzCiqMshK1FPSLpWO7garj5u3
+YnnFDKppvt//AEFRTHVYacgQ0udQSMynWGS4tzzqxUtydyQTiq0YfpseHLWawaYC6u3kPoCvub4O
+OWvpbI4ezbTdJxC0r1XhIx1aW8oUn1v/AJvivPzI39DsdHx5PtUVPoneOVpayRKlKZfCaJiCdyTv
+ck2F/uMaXKnpavYiiMnW1Y9OwqxTtO6C0ynQoIuo1AA78/8AGHy8aL6YN0eM16rr5qeHxggpp0Cz
+aYwLMrndTyLGx9MQvHjvfR1+hPramevr6iaSZpJJZS2vgsext9hixGKjFJE22fZbltZmtctLQwy1
+FQ+yJGNRv+1vfHTyQhHnkdImMJSfFLZevw3+EUcEyV2czh6lfkVR5EbtYnk+/tjB8n4m8v0YVS/c
+u4vGUPql2XFJl5MAWYaibD5f8tjIk0XEjNNkSIxa2jfa/wCWBa+xNkHPMnSajdbgEIwIbv6XHpgV
+qXJBJXo5zqsnEWdGjIWS8oVSG+XU9h9bb3HbHpoZ+WHn+RQljqVEHqOkENFDSqiMq1UxW3JF7H7X
+Xj64d487lyv0hWRar8wJk0arVCJ2RUJJ1MdlbixPobYt5HcW0V0iysiyTMa6ro2pqOi0RvdHjk1K
++2wIBJIxk5pwjabewoxd2hpHRvU1TmFFXVGZapoTdjqKge3Nj2H0AxXl5eOMWlHsOOCTY7ZjUTUm
+VwrLL+JrVGyKCdbD1P8ALz3OMzDH63JaTLkp8Y17KG6k8OOKopKGIvII1GuxBtxb2G55/vj0HjqT
+lym/ZTlVUhPqqA/wWREVS8Uha+m+w2IB/LjGgprmhDTI2awuuVUxmLqI49Kqex1E2PvucdGVzdE1
+SsG0ccktSqxX1Dvbgep/PBt0rOirejpj4C9IJQ5TLWTxXnqW1K9iCYx8ot6Hn7jHmfifkPNlUU9L
++TVwY/lw/Nl009Ivg2CgWsRccHFSNpWiW7dGWijuBGLXbAPYa12bWj0wGx78WxMlUQU9i91AqpSz
+CUnwyp498VcjldD8Zz7EIaTOqd6dkll/EukjWuCQbkqSeNyfXtvj0D5TxvkvVlZ1FiP1DXvJmwic
+qRCWN/fUf73xqePjUcdr2UMsvqPa5LVVVG88dNK8RYBNKni3Jt62wXz4xbi2FxbV0Fum80qKevSn
+qZXpp1kChilib8bYR5GFSi5R2LafRfGW9RR1FHFBUxzyGMJTuIxpAfgAXtfb19748/ODT2WYz0e6
+l6GOnWVzXxr4nhp8urUb3BBa/wBzhP1ctfuE6rZVOe0aNCskauXSO7Kw0rIO2/pe36Y1sGRp02Vp
+xdWKeeS02V5PGs0aLUMRaPuSMaGLnlyWnoBpRVsQayplq5DJMQVDHSnZfYY0IxSWhTdlp/B3pMZt
+RzVksPiXa2+1gP2A2NxvjH+JeY8clCJf8TEmuTOm+k8pGU5VBT69ciLubW1H1AxhOTcm37LcpWHw
+BYDa/e+Du9AL7mDGRp3IOq9sQ4vom/Z9UJqW1rEbHvgZHJ7EvrVo48qqBJJa6FTcHk9vU4RdzikW
+I9MpSopZ6itCUdGoUEkuyhUDXW9zfbgbcXxtQmlG5S3/AOCrkT6iiZknw5paurmq5JRmFbK5Z1SI
+yxq1727Ltfe57YXk+JTSUIql+4C8ZW5NhvOsloaOkZMwzGKIR/8AtJNVrCL8GyxrueRtfAYcuSTk
+4x3+l/yFOMUtspvq5MtjrUejqF8YfM0c0rgenzqP0ON7xnkcWpr+DPy1eiT0v17XZUwhqJmeEEBJ
+lVQwP9RNwdu/IwryPAhkuUdMnHlcdMtTJ/iHPDCGzFEZJPKr+EmpbDctpA59TvjMn4bb+mi1GQKp
+0myd2rM8rqengC6pUldW2K7WUEkX+vNhbA5az/Rhi2/0ohfRuWim+r83Gd59PVxK0dKvkp47Wsg9
+vfnHofGwfIxqDdv2Uck+UrQMjjaQrCpbzDg84baQNM6J+Due02Vwfgc5XRS2DxS6SFF+dQ/Kx+2P
+M/EcDlk+bD/Zq+PlShxZfFPXUtTRGqpqiOSMjV4isCCPrx3xm8lX2D4uyTSFpRrIIU4OKs6TrROA
+TQe/IGHqOhLbs1SeGgYHn62H1wuSDTbK862rqGOOZDLrYA/JJpAJG4v22v72xWWJzmmh/wAxQVMq
+PMetMloJJHaMZhU2IjhtaFe3H83bnGvj+HZsi/xX7lV+RFO3sWM++JOdVkfgzVy5fSqDppqRLEDi
+5tsP0Pti/g+F4obrl+bEy8lvrQjVuc1Uxv8AiZyp/pA3+u+NKOCEVVFdzb7YPeeRyLs7G9zra+DS
+SAb9mI9QG1irHcDg46jhx6Unlqf/AEzYTIC8bjctYElSODtc7+n0xUz41H+5/wB/UtYJuX0P/v5A
+3rmeq/i38PqleMU41OjC12Nz+gNh9cT4UY8OcfYnNafF+hZbbzXv7W74t0IDmQQJJI81TfSGAuDx
+te+/+cYRlbWkGl9xsjzYitpIqYMksqDQNfyKCbk32vYHn/5DFT5Sabl6GqdPQcyjNJTI6ZfO1KsV
+tbU8ugsx33HDADfj88V8mBNXJXY6GV9Idss+IWf5epi/iaT6AAyVFOCRe1hdbeo5v2xVfiQ7imv9
+jVlT7C8HxZro95IKCcKbF4y4J/Q/TAf0kv8AIJzh3REz74pVE1NIbU9DDGt5JSS+/ZFIFy1+w3+g
+3wEfDcpV2BLyIpfSVD1D1f40FU5m8JpzaOJV8SZ151MSdMYNztuxxq4PD4ta6/4/19ytPNYjPXSy
+E+CCt99iSxPqTycaKikV07C2X5bClI1bVgtHTjdb7SPvZfzt+uFSnLkox7YVLshVcaRKseka7NyN
+gbL/AM4bFtnPQLWOQm6q5ABJ8p49T7e+D5AbZ6VvmNh2sL8Y6yVYRy+qemqaadLiSFw4sObdt8RJ
+cotP2HGTi7XZ/9k=
diff --git a/testing/resources/multiple_form_types.in b/testing/resources/multiple_form_types.in
new file mode 100644
index 0000000..ec6af63
--- /dev/null
+++ b/testing/resources/multiple_form_types.in
@@ -0,0 +1,131 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /AcroForm <<
+ /Fields [8 0 R 9 0 R 10 0 R 11 0 R 12 0 R]
+ /DR 4 0 R
+ >>
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Resources 4 0 R
+ /MediaBox [0 0 300 600]
+ /Contents 6 0 R
+ /Annots [7 0 R 8 0 R 9 0 R 10 0 R 11 0 R 13 0 R]
+>>
+endobj
+{{object 4 0}} <<
+ /Font <<
+ /F1 5 0 R
+ >>
+>>
+endobj
+{{object 5 0}} <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Helvetica
+>>
+endobj
+{{object 6 0}} <<
+ {{streamlen}}
+>>
+stream
+BT
+0 0 0 rg
+/F1 12 Tf
+100 450 Td
+(Test Form) Tj
+ET
+endstream
+endobj
+{{object 7 0}} <<
+ /Type /Annot
+ /Subtype /Link
+ /BS <<
+ /W 0
+ >>
+ /Rect [100 450 160 460]
+ /A <<
+ /Type /Action
+ /URI (https://www.cs.chromium.org/)
+ /S /URI
+ >>
+ /F 4
+>>
+endobj
+{{object 8 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Ch
+ /Ff 393216
+ /T (Combo_Editable)
+ /DA (0 0 0 rg /F1 12 Tf)
+ /Rect [100 400 200 430]
+ /Opt [[(foo) (Foo)] [(bar) (Bar)] [(qux) (Qux)]]
+>>
+endobj
+{{object 9 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Ch
+ /Ff 2097152
+ /T (Listbox_MultiSelectMultipleSelected)
+ /DA (0 0 0 rg /F1 12 Tf)
+ /Rect [100 350 200 380]
+ /Opt [(Alpha) (Beta) (Gamma) (Delta) (Epsilon)]
+ /V [(Epsilon) (Gamma)]
+>>
+endobj
+{{object 10 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Tx
+ /T (Text Box)
+ /DA (0 0 0 rg /F1 12 Tf)
+ /Rect [100 300 200 330]
+>>
+endobj
+{{object 11 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Btn
+ /F 4
+ /P 3 0 R
+ /Rect [135 250 155 270]
+ /T (Checkbox)
+>>
+endobj
+{{object 12 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Btn
+ /Ff 32768
+ /T (radioButton)
+ /TU (radioButton1)
+ /Kids [13 0 R]
+ /V /value3
+>>
+endobj
+{{object 13 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Btn
+ /F 4
+ /P 3 0 R
+ /Parent 12 0 R
+ /Rect [85 50 105 70]
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/multiple_form_types.pdf b/testing/resources/multiple_form_types.pdf
new file mode 100644
index 0000000..da83c9e
--- /dev/null
+++ b/testing/resources/multiple_form_types.pdf
@@ -0,0 +1,151 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /AcroForm <<
+ /Fields [8 0 R 9 0 R 10 0 R 11 0 R 12 0 R]
+ /DR 4 0 R
+ >>
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Resources 4 0 R
+ /MediaBox [0 0 300 600]
+ /Contents 6 0 R
+ /Annots [7 0 R 8 0 R 9 0 R 10 0 R 11 0 R 13 0 R]
+>>
+endobj
+4 0 obj <<
+ /Font <<
+ /F1 5 0 R
+ >>
+>>
+endobj
+5 0 obj <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Helvetica
+>>
+endobj
+6 0 obj <<
+ /Length 51
+>>
+stream
+BT
+0 0 0 rg
+/F1 12 Tf
+100 450 Td
+(Test Form) Tj
+ET
+endstream
+endobj
+7 0 obj <<
+ /Type /Annot
+ /Subtype /Link
+ /BS <<
+ /W 0
+ >>
+ /Rect [100 450 160 460]
+ /A <<
+ /Type /Action
+ /URI (https://www.cs.chromium.org/)
+ /S /URI
+ >>
+ /F 4
+>>
+endobj
+8 0 obj <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Ch
+ /Ff 393216
+ /T (Combo_Editable)
+ /DA (0 0 0 rg /F1 12 Tf)
+ /Rect [100 400 200 430]
+ /Opt [[(foo) (Foo)] [(bar) (Bar)] [(qux) (Qux)]]
+>>
+endobj
+9 0 obj <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Ch
+ /Ff 2097152
+ /T (Listbox_MultiSelectMultipleSelected)
+ /DA (0 0 0 rg /F1 12 Tf)
+ /Rect [100 350 200 380]
+ /Opt [(Alpha) (Beta) (Gamma) (Delta) (Epsilon)]
+ /V [(Epsilon) (Gamma)]
+>>
+endobj
+10 0 obj <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Tx
+ /T (Text Box)
+ /DA (0 0 0 rg /F1 12 Tf)
+ /Rect [100 300 200 330]
+>>
+endobj
+11 0 obj <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Btn
+ /F 4
+ /P 3 0 R
+ /Rect [135 250 155 270]
+ /T (Checkbox)
+>>
+endobj
+12 0 obj <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Btn
+ /Ff 32768
+ /T (radioButton)
+ /TU (radioButton1)
+ /Kids [13 0 R]
+ /V /value3
+>>
+endobj
+13 0 obj <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Btn
+ /F 4
+ /P 3 0 R
+ /Parent 12 0 R
+ /Rect [85 50 105 70]
+>>
+endobj
+xref
+0 14
+0000000000 65535 f
+0000000015 00000 n
+0000000149 00000 n
+0000000212 00000 n
+0000000377 00000 n
+0000000428 00000 n
+0000000504 00000 n
+0000000606 00000 n
+0000000798 00000 n
+0000001002 00000 n
+0000001252 00000 n
+0000001387 00000 n
+0000001514 00000 n
+0000001663 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 14
+>>
+startxref
+1788
+%%EOF
diff --git a/testing/resources/named_dests.in b/testing/resources/named_dests.in
index a97aebe..c71f2cc 100644
--- a/testing/resources/named_dests.in
+++ b/testing/resources/named_dests.in
@@ -64,8 +64,8 @@
>>
endobj
% Old-style top-level Dests dictionary. Note that FirstAlternate
-% intentionally references non-exisstant page 11 and LastAlternate
-% intentionally references non-existant object 999.
+% intentionally references non-existent page 11 and LastAlternate
+% intentionally references non-existent object 999.
{{object 14 0}} <<
/FirstAlternate [11 /XYZ 200 400 800]
/LastAlternate <</D [999 0 R /XYZ 0 0 -200]>>
diff --git a/testing/resources/named_dests.pdf b/testing/resources/named_dests.pdf
index b99cead..ddcc985 100644
--- a/testing/resources/named_dests.pdf
+++ b/testing/resources/named_dests.pdf
@@ -65,8 +65,8 @@
>>
endobj
% Old-style top-level Dests dictionary. Note that FirstAlternate
-% intentionally references non-exisstant page 11 and LastAlternate
-% intentionally references non-existant object 999.
+% intentionally references non-existent page 11 and LastAlternate
+% intentionally references non-existent object 999.
14 0 obj <<
/FirstAlternate [11 /XYZ 200 400 800]
/LastAlternate <</D [999 0 R /XYZ 0 0 -200]>>
@@ -117,16 +117,19 @@
0000000638 00000 n
0000000766 00000 n
0000000000 65535 f
-0000001060 00000 n
-0000001188 00000 n
+0000001059 00000 n
+0000001187 00000 n
0000000000 65535 f
0000000000 65535 f
0000000000 65535 f
0000000000 65535 f
0000000000 65535 f
-0000001283 00000 n
-0000001393 00000 n
-trailer<< /Root 1 0 R /Size 23 >>
+0000001282 00000 n
+0000001392 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 23
+>>
startxref
-1481
+1480
%%EOF
diff --git a/testing/resources/named_dests_old_style.in b/testing/resources/named_dests_old_style.in
new file mode 100644
index 0000000..70e45ed
--- /dev/null
+++ b/testing/resources/named_dests_old_style.in
@@ -0,0 +1,55 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /Dests 6 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 2
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Resources <<
+ /Font <<
+ /F1 5 0 R
+ >>
+ >>
+ /Contents [4 0 R]
+ /MediaBox [0 0 612 792]
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+BT
+/F1 20 Tf
+100 600 TD (Page1)Tj
+ET
+endstream
+endobj
+{{object 5 0}} <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Arial
+>>
+endobj
+% Old-style top-level Dests dictionary. Note that FirstAlternate
+% intentionally references non-existent page 11 and LastAlternate
+% intentionally references non-existent object 999.
+{{object 6 0}} <<
+ /FirstAlternate [11 /XYZ 200 400 800]
+ /LastAlternate <<
+ /D [999 0 R /XYZ 0 0 -200]
+ >>
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/named_dests_old_style.pdf b/testing/resources/named_dests_old_style.pdf
new file mode 100644
index 0000000..df7f04a
--- /dev/null
+++ b/testing/resources/named_dests_old_style.pdf
@@ -0,0 +1,68 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /Dests 6 0 R
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /Count 2
+ /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Resources <<
+ /Font <<
+ /F1 5 0 R
+ >>
+ >>
+ /Contents [4 0 R]
+ /MediaBox [0 0 612 792]
+>>
+endobj
+4 0 obj <<
+ /Length 37
+>>
+stream
+BT
+/F1 20 Tf
+100 600 TD (Page1)Tj
+ET
+endstream
+endobj
+5 0 obj <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Arial
+>>
+endobj
+% Old-style top-level Dests dictionary. Note that FirstAlternate
+% intentionally references non-existent page 11 and LastAlternate
+% intentionally references non-existent object 999.
+6 0 obj <<
+ /FirstAlternate [11 /XYZ 200 400 800]
+ /LastAlternate <<
+ /D [999 0 R /XYZ 0 0 -200]
+ >>
+>>
+endobj
+xref
+0 7
+0000000000 65535 f
+0000000015 00000 n
+0000000083 00000 n
+0000000146 00000 n
+0000000300 00000 n
+0000000388 00000 n
+0000000643 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 7
+>>
+startxref
+761
+%%EOF
diff --git a/testing/resources/no_page_count.in b/testing/resources/no_page_count.in
new file mode 100644
index 0000000..e6b1a2d
--- /dev/null
+++ b/testing/resources/no_page_count.in
@@ -0,0 +1,50 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /MediaBox [0 0 200 200]
+ /Kids [3 0 R 3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Pages
+ /Kids [4 0 R 4 0 R 4 0 R]
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Resources <<
+ /Font <<
+ /F1 5 0 R
+ >>
+ >>
+ /Contents 6 0 R
+>>
+endobj
+{{object 5 0}} <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Times-Roman
+>>
+endobj
+
+{{object 6 0}} <<
+ {{streamlen}}
+>>
+stream
+BT
+20 50 Td
+/F1 12 Tf
+(Hello, world!) Tj
+ET
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/no_page_count.pdf b/testing/resources/no_page_count.pdf
new file mode 100644
index 0000000..885df8c
--- /dev/null
+++ b/testing/resources/no_page_count.pdf
@@ -0,0 +1,63 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /MediaBox [0 0 200 200]
+ /Kids [3 0 R 3 0 R]
+>>
+endobj
+3 0 obj <<
+ /Type /Pages
+ /Kids [4 0 R 4 0 R 4 0 R]
+>>
+endobj
+4 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Resources <<
+ /Font <<
+ /F1 5 0 R
+ >>
+ >>
+ /Contents 6 0 R
+>>
+endobj
+5 0 obj <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Times-Roman
+>>
+endobj
+
+6 0 obj <<
+ /Length 44
+>>
+stream
+BT
+20 50 Td
+/F1 12 Tf
+(Hello, world!) Tj
+ET
+endstream
+endobj
+xref
+0 7
+0000000000 65535 f
+0000000015 00000 n
+0000000068 00000 n
+0000000152 00000 n
+0000000216 00000 n
+0000000342 00000 n
+0000000421 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 7
+>>
+startxref
+516
+%%EOF
diff --git a/testing/resources/non_hex_file_id.in b/testing/resources/non_hex_file_id.in
new file mode 100644
index 0000000..7a93439
--- /dev/null
+++ b/testing/resources/non_hex_file_id.in
@@ -0,0 +1,26 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /MediaBox [ 0 0 200 200 ]
+ /Count 1
+ /Kids [ 3 0 R ]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+>>
+endobj
+{{xref}}
+trailer <<
+ /Root 1 0 R
+ /ID [(permanent non-hex) (changing non-hex)]
+ {{trailersize}}
+>>
+{{startxref}}
+%%EOF
diff --git a/testing/resources/non_hex_file_id.pdf b/testing/resources/non_hex_file_id.pdf
new file mode 100644
index 0000000..6f11eb6
--- /dev/null
+++ b/testing/resources/non_hex_file_id.pdf
@@ -0,0 +1,33 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /MediaBox [ 0 0 200 200 ]
+ /Count 1
+ /Kids [ 3 0 R ]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+>>
+endobj
+xref
+0 4
+0000000000 65535 f
+0000000015 00000 n
+0000000068 00000 n
+0000000161 00000 n
+trailer <<
+ /Root 1 0 R
+ /ID [(permanent non-hex) (changing non-hex)]
+ /Size 4
+>>
+startxref
+212
+%%EOF
diff --git a/testing/resources/page_tree_empty_node.in b/testing/resources/page_tree_empty_node.in
new file mode 100644
index 0000000..4a9b901
--- /dev/null
+++ b/testing/resources/page_tree_empty_node.in
@@ -0,0 +1,40 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /MediaBox [0 0 200 200]
+ /Kids [3 0 R 4 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Pages
+ /Count 0
+ /Kids []
+ /Parent 2 0 R
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Pages
+ /Count 2
+ /Kids [5 0 R 6 0 R]
+ /Parent 2 0 R
+>>
+endobj
+{{object 5 0}} <<
+ /Type /Page
+ /Parent 4 0 R
+>>
+endobj
+{{object 6 0}} <<
+ /Type /Page
+ /Parent 4 0 R
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/page_tree_empty_node.pdf b/testing/resources/page_tree_empty_node.pdf
new file mode 100644
index 0000000..bf80a1a
--- /dev/null
+++ b/testing/resources/page_tree_empty_node.pdf
@@ -0,0 +1,53 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /MediaBox [0 0 200 200]
+ /Kids [3 0 R 4 0 R]
+>>
+endobj
+3 0 obj <<
+ /Type /Pages
+ /Count 0
+ /Kids []
+ /Parent 2 0 R
+>>
+endobj
+4 0 obj <<
+ /Type /Pages
+ /Count 2
+ /Kids [5 0 R 6 0 R]
+ /Parent 2 0 R
+>>
+endobj
+5 0 obj <<
+ /Type /Page
+ /Parent 4 0 R
+>>
+endobj
+6 0 obj <<
+ /Type /Page
+ /Parent 4 0 R
+>>
+endobj
+xref
+0 7
+0000000000 65535 f
+0000000015 00000 n
+0000000068 00000 n
+0000000152 00000 n
+0000000226 00000 n
+0000000311 00000 n
+0000000362 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 7
+>>
+startxref
+413
+%%EOF
diff --git a/testing/resources/pixel/axial_shading_point_at_border_no_extend_expected_skia.pdf.0.png b/testing/resources/pixel/axial_shading_point_at_border_no_extend_expected_skia.pdf.0.png
new file mode 100644
index 0000000..6c4c9b7
--- /dev/null
+++ b/testing/resources/pixel/axial_shading_point_at_border_no_extend_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1012369.in b/testing/resources/pixel/bug_1012369.in
index 7f56d35..db1fc5a 100644
--- a/testing/resources/pixel/bug_1012369.in
+++ b/testing/resources/pixel/bug_1012369.in
@@ -46,7 +46,7 @@
/Filter [/ASCIIHexDecode /JPXDecode]
/Height 64
/Width 64
-{{streamlen}}
+ {{streamlen}}
>>
stream
0000000c6a5020200d0a870a00000014667479706a703220000000006a7032200000004f6a7032
@@ -67,7 +67,7 @@
/Filter [/ASCIIHexDecode /JPXDecode]
/Height 64
/Width 64
-{{streamlen}}
+ {{streamlen}}
>>
stream
0000000c6a5020200d0a870a00000014667479706a703220000000006a7032200000004f6a7032
diff --git a/testing/resources/pixel/bug_1015233.in b/testing/resources/pixel/bug_1015233.in
new file mode 100644
index 0000000..aba521f
--- /dev/null
+++ b/testing/resources/pixel/bug_1015233.in
@@ -0,0 +1,113 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /MediaBox [0 0 600 200]
+ /Resources <<
+ /ProcSet [/PDF /ImageC]
+ /ColorSpace <<
+ /C1 [/Pattern]
+ >>
+ /Pattern <<
+ /P1 5 0 R
+ >>
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+0.24 0 0 0.24 0 0 cm
+/C1 cs
+/P1 scn
+888 400 108 60 re
+f*
+0 0 0 rg
+888 460 m
+888 400 l
+S
+/C1 cs
+/P1 scn
+1536 400 108 60 re
+f*
+0 0 0 rg
+1536 460 m
+1536 400 l
+S
+/C1 cs
+/P1 scn
+2184 400 108 60 re
+f*
+0 0 0 rg
+2184 460 m
+2184 400 l
+S
+Q
+endstream
+endobj
+{{object 5 0}} <<
+ /Type /Pattern
+ /PaintType 1
+ /PatternType 1
+ /TilingType 1
+ /BBox [0 0 128 32]
+ /Matrix [0.202488 0 0 -0.449947 0 791.999]
+ /Resources <<
+ /ProcSet [/PDF /ImageC]
+ /XObject <<
+ /R17 6 0 R
+ >>
+ >>
+ /XStep 128
+ /YStep 32
+ {{streamlen}}
+>>
+stream
+0.24 0 0 0.24 0 0 cm
+q
+0 134 534 -134 re
+W n
+q 533.333 0 0 -133.333 0 133.333 cm
+/R17 Do
+Q
+Q
+endstream
+endobj
+{{object 6 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 8
+ /ColorSpace /DeviceRGB
+ /DecodeParms <<
+ /Columns 128
+ /Colors 3
+ /Predictor 15
+ >>
+ /Filter [/ASCII85Decode /FlateDecode]
+ /Height 32
+ /Width 128
+ {{streamlen}}
+>>
+stream
+Gb"0JYmJ3"&-Ti)eYrN1j:2;&T!_s\O.TqV*k'Yd:%,W`r[n(QqR?*8l/0q(V12/<QMrK7>Pbi$Ds?0-
+^SQEPrg*O8rEK--oPa!Od_DW.CH2@W5h#c57fSeR/cAoM\+FV'hpVPkGl`cd*^p
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1015233_expected.pdf.0.png b/testing/resources/pixel/bug_1015233_expected.pdf.0.png
new file mode 100644
index 0000000..aa39e7e
--- /dev/null
+++ b/testing/resources/pixel/bug_1015233_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1015233_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1015233_expected_skia.pdf.0.png
new file mode 100644
index 0000000..1a2a736
--- /dev/null
+++ b/testing/resources/pixel/bug_1015233_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1021762_expected.pdf.0.png b/testing/resources/pixel/bug_1021762_expected.pdf.0.png
index 39d531a..6c436c7 100644
--- a/testing/resources/pixel/bug_1021762_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_1021762_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1072440.in b/testing/resources/pixel/bug_1072440.in
new file mode 100644
index 0000000..1ef4fd6
--- /dev/null
+++ b/testing/resources/pixel/bug_1072440.in
@@ -0,0 +1,39 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /AcroForm <<
+ /Fields [4 0 R]
+ >>
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Annots [4 0 R]
+ /MediaBox [0 0 200 200]
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Tx
+ /F 4
+ /Ff 8388608
+ /MaxLen 2
+ /P 3 0 R
+ /Rect [70 90 130 110]
+ /T (zip code)
+ /V (11111)
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1072440_expected.pdf.0.png b/testing/resources/pixel/bug_1072440_expected.pdf.0.png
new file mode 100644
index 0000000..ef8b63d
--- /dev/null
+++ b/testing/resources/pixel/bug_1072440_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1072440_expected_mac.pdf.0.png b/testing/resources/pixel/bug_1072440_expected_mac.pdf.0.png
new file mode 100644
index 0000000..28d1ef0
--- /dev/null
+++ b/testing/resources/pixel/bug_1072440_expected_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1072440_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1072440_expected_skia.pdf.0.png
new file mode 100644
index 0000000..6ce4fbe
--- /dev/null
+++ b/testing/resources/pixel/bug_1072440_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1099446.in b/testing/resources/pixel/bug_1099446.in
new file mode 100644
index 0000000..49d6dae
--- /dev/null
+++ b/testing/resources/pixel/bug_1099446.in
@@ -0,0 +1,65 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Contents 4 0 R
+ /MediaBox [0 0 200 200]
+ /Resources <<
+ /ExtGState <<
+ /G1 5 0 R
+ >>
+ /Pattern <<
+ /P1 6 0 R
+ >>
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+200 0 m
+0 200 l
+/G1 gs
+/P1 SCN
+/P1 S
+endstream
+endobj
+{{object 5 0}} <<
+ /BM /Diff
+>>
+endobj
+{{object 6 0}} <<
+ /Type /Pattern
+ /PatternType 1
+ /Resources <<
+ /ExtGState <<
+ /G1 5 0 R
+ >>
+ /Pattern <<
+ /P1 6 0 R
+ >>
+ >>
+ {{streamlen}}
+>>
+stream
+200 0 m
+0 200 l
+/G3 gs
+/P1 S
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1099446_expected.pdf.0.png b/testing/resources/pixel/bug_1099446_expected.pdf.0.png
new file mode 100644
index 0000000..1074bd8
--- /dev/null
+++ b/testing/resources/pixel/bug_1099446_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1116869.in b/testing/resources/pixel/bug_1116869.in
new file mode 100644
index 0000000..d123e38
--- /dev/null
+++ b/testing/resources/pixel/bug_1116869.in
@@ -0,0 +1,117 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 5 0 R
+ /MediaBox [0 0 400 400]
+ /Resources <<
+ /Font <<
+ /F1 4 0 R
+ >>
+ /Pattern <<
+ /P1 6 0 R
+ >>
+ /XObject <<
+ /X10 9 0 R
+ >>
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Times-Roman
+>>
+endobj
+{{object 5 0}} <<
+ {{streamlen}}
+>>
+stream
+/X10 Do
+endstream
+endobj
+{{object 6 0}} <<
+ /Type /Pattern
+ /PatternType 2
+ /Shading 7 0 R
+>>
+endobj
+{{object 7 0}} <<
+ /ShadingType 2
+ /ColorSpace /DeviceCMYK
+ /Coords [0.0 0.0 1.0 0.0]
+ /Extend [true true]
+ /Function 8 0 R
+>>
+endobj
+{{object 8 0}} <<
+ /FunctionType 0
+ /BitsPerSample 1
+ /Domain [0.0 1.0]
+ /Filter /ASCIIHexDecode
+ /Range [0.0 1.0 0.0 1.0 0.0 1.0 0.0 1.0]
+ /Size [2]
+ {{streamlen}}
+>>
+stream
+ff
+endstream
+endobj
+{{object 9 0}} <<
+ /Subtype /Form
+ /Resources <<
+ /ExtGState <<
+ /G7 10 0 R
+ >>
+ >>
+ {{streamlen}}
+>>
+stream
+q
+0 0 m
+3 31 l
+/G7 gs
+f
+endstream
+endobj
+{{object 10 0}} <<
+ /SMas <<
+ /G 11 0 R
+ >>
+>>
+endobj
+{{object 11 0}} <<
+ {{streamlen}}
+>>
+stream
+1 1 2 2 re
+W
+100 100 200 200 re s
+W
+0 g
+/F1 12 Tf
+0 0 60 30 re
+W
+m 100 100
+130 150 200 200 250 230 c
+/Pattern cs
+/P1 scn
+(ABC) Tj
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1116869_expected.pdf.0.png b/testing/resources/pixel/bug_1116869_expected.pdf.0.png
new file mode 100644
index 0000000..1ff3f1f
--- /dev/null
+++ b/testing/resources/pixel/bug_1116869_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1128284.in b/testing/resources/pixel/bug_1128284.in
new file mode 100644
index 0000000..0dfda97
--- /dev/null
+++ b/testing/resources/pixel/bug_1128284.in
@@ -0,0 +1,94 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /Resources <<
+ /Font <<
+ /F1 5 0 R
+ >>
+ >>
+ /MediaBox [0 0 200 200]
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+0 g
+BT
+/F1 .1 Tf 1 0 0 1 100 100 Tm
+[(a)16000(b)17000(c)]TJ
+ET
+endstream
+endobj
+{{object 5 0}} <<
+ /Type /Font
+ /Subtype /Type3
+ /CharProcs <<
+ /a97 6 0 R
+ /a98 7 0 R
+ /a99 6 0 R
+ >>
+ /Encoding 8 0 R
+ /FirstChar 97
+ /FontBBox [0 0 100 100]
+ /LastChar 99
+ /Widths [-100 -50 -75]
+>>
+endobj
+{{object 6 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+6 0 0 48 6 0 cm
+BI
+/W 6
+/H 48
+/BPC 1
+/IM true
+ID
+xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+EI
+Q
+endstream
+endobj
+{{object 7 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+6 0 0 48 6 0 cm
+BI
+/W 6
+/H 48
+/BPC 1
+/IM true
+ID
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+EI
+Q
+endstream
+endobj
+{{object 8 0}} <<
+ /Type /Encoding
+ /BaseEncoding /WinAnsiEncoding
+ /Differences [97 /a97 /a98 /a99]
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1128284_expected.pdf.0.png b/testing/resources/pixel/bug_1128284_expected.pdf.0.png
new file mode 100644
index 0000000..6a0df39
--- /dev/null
+++ b/testing/resources/pixel/bug_1128284_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_113910_expected.pdf.0.png b/testing/resources/pixel/bug_113910_expected.pdf.0.png
index 57c6f08..250ea73 100644
--- a/testing/resources/pixel/bug_113910_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_113910_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_113910_expected_mac.pdf.0.png b/testing/resources/pixel/bug_113910_expected_mac.pdf.0.png
index 5b21f5a..f6d8fca 100644
--- a/testing/resources/pixel/bug_113910_expected_mac.pdf.0.png
+++ b/testing/resources/pixel/bug_113910_expected_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_113910_expected_skia.pdf.0.png b/testing/resources/pixel/bug_113910_expected_skia.pdf.0.png
new file mode 100644
index 0000000..3c3cd81
--- /dev/null
+++ b/testing/resources/pixel/bug_113910_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_113910_expected_win.pdf.0.png b/testing/resources/pixel/bug_113910_expected_win.pdf.0.png
deleted file mode 100644
index 1ff9194..0000000
--- a/testing/resources/pixel/bug_113910_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_1161.in b/testing/resources/pixel/bug_1161.in
index 1f02ab5..f3317e1 100644
--- a/testing/resources/pixel/bug_1161.in
+++ b/testing/resources/pixel/bug_1161.in
@@ -21,7 +21,7 @@
>>
endobj
{{object 5 0}} <<
-{{streamlen}}
+ {{streamlen}}
>>
stream
q 1 0 0 1 50 50 cm /X0 Do Q
@@ -48,20 +48,20 @@
endstream
endobj
{{object 7 0}} <<
-/Type /ExtGState
-/ca 0.2
-/CA 1
+ /Type /ExtGState
+ /ca 0.2
+ /CA 1
>>
endobj
{{object 8 0}} <<
-/Type /Pattern
-/TilingType 1
-/PatternType 1
-/BBox [0 0 18 18]
-/PaintType 1
-/YStep 18
-/XStep 18
-{{streamlen}}
+ /Type /Pattern
+ /TilingType 1
+ /PatternType 1
+ /BBox [0 0 18 18]
+ /PaintType 1
+ /YStep 18
+ /XStep 18
+ {{streamlen}}
>>
stream
0 0 0 rg
diff --git a/testing/resources/pixel/bug_1161_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1161_expected_skia.pdf.0.png
new file mode 100644
index 0000000..f6a4fee
--- /dev/null
+++ b/testing/resources/pixel/bug_1161_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1236805.in b/testing/resources/pixel/bug_1236805.in
new file mode 100644
index 0000000..564f048
--- /dev/null
+++ b/testing/resources/pixel/bug_1236805.in
@@ -0,0 +1,86 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Contents 6 0 R
+ /CropBox [0 0 612 792]
+ /MediaBox [0 0 612 792]
+ /Parent 2 0 R
+ /Resources 7 0 R
+>>
+endobj
+{{object 6 0}} <<
+ {{streamlen}}
+ >>
+stream
+BT
+/T4 1 Tf
+0.12 0 0 -0.12 99.84 684.1801 Tm
+0 g
+/GS1 gs
+0 Tc
+0 Tw
+()Tj
+ET
+endstream
+endobj
+{{object 7 0}} <<
+ /Font <<
+ /T4 11 0 R
+ >>
+ /ProcSet [/PDF /Text]
+>>
+endobj
+{{object 11 0}} <<
+ /Type /Font
+ /Subtype /Type3
+ /CharProcs 12 0 R
+ /Encoding 13 0 R
+ /FirstChar 0
+ /FontBBox [4 -19 53 56]
+ /FontMatrix [1 0.0112920878908041593329204334 0 -1 0 0]
+ /LastChar 4
+ /Name /T4
+ /Widths [60 38 38 38 21]
+>>
+endobj
+{{object 12 0}} <<
+ /CV 17 0 R
+>>
+endobj
+{{object 13 0}} <<
+ /Type /Encoding
+ /Differences [0 /A0 /CU /CV /D2 /CY]
+ >>
+endobj
+{{object 17 0}} <<
+ {{streamlen}}
+>>
+stream
+38 0 5 -19 32 56 d1
+q
+27 0 0 75 5.1 -19.1 cm
+BI
+/W 27
+/H 75
+/BPC 1
+/IM true
+ID
+EI
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1236805_expected.pdf.0.png b/testing/resources/pixel/bug_1236805_expected.pdf.0.png
new file mode 100644
index 0000000..7d4eebe
--- /dev/null
+++ b/testing/resources/pixel/bug_1236805_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1236_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1236_expected_skia.pdf.0.png
new file mode 100644
index 0000000..248125f
--- /dev/null
+++ b/testing/resources/pixel/bug_1236_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1237898.in b/testing/resources/pixel/bug_1237898.in
new file mode 100644
index 0000000..c45e4c6
--- /dev/null
+++ b/testing/resources/pixel/bug_1237898.in
@@ -0,0 +1,60 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Annots [4 0 R]
+ /MediaBox [0 0 100 100]
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Btn
+ /Ff 65536
+ /P 3 0 R
+ /BS <<
+ /S /S
+ /W 1
+ >>
+ /MK <<
+ /I 5 0 R
+ >>
+ /N 6 0 R
+ /Rect [1 2 3 4]
+ /T (foo)
+ /TU (bar)
+>>
+endobj
+{{object 5 0}} <<
+ /Type /XObject
+ /Name /F#e9M
+ /FormType 1
+ {{streamlen}}
+>>
+stream
+endstream
+endobj
+{{object 6 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ {{streamlen}}
+>>
+stream
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1237898_expected.pdf.0.png b/testing/resources/pixel/bug_1237898_expected.pdf.0.png
new file mode 100644
index 0000000..319932a
--- /dev/null
+++ b/testing/resources/pixel/bug_1237898_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1241587.in b/testing/resources/pixel/bug_1241587.in
new file mode 100644
index 0000000..61b1637
--- /dev/null
+++ b/testing/resources/pixel/bug_1241587.in
@@ -0,0 +1,78 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 100 100]
+ /Resources <<
+ /Font <<
+ /F1 5 0 R
+ >>
+ >>
+ /Contents 4 0 R
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+BT
+/F1 1 Tf
+(s) Tj
+ET
+endstream
+endobj
+{{object 5 0}} <<
+ /Type /Font
+ /Subtype /Type3
+ /CharProcs <<
+ /s 6 0 R
+ >>
+ /Encoding <<
+ /Differences [115 /s]
+ >>
+ /FirstChar 115
+ /FontBBox [0 0 1 2]
+ /LastChar 115
+ /Resources <<
+ /XObject <<
+ /Im1 7 0 R
+ >>
+ >>
+>>
+endobj
+{{object 6 0}} <<
+ {{streamlen}}
+>>
+stream
+/Im1 Do
+endstream
+endobj
+{{object 7 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /Width 50
+ /Height 50
+ /BitsPerComponent 8
+ /ColorSpace /DeviceRGB
+ /Filter [/ASCIIHexDecode /FlateDecode]
+ {{streamlen}}
+>>
+stream
+789cedc2310d00000c03a07f2aaab3ea7bcf03842655555555555555f5bf01cc7818dc
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1241587_expected.pdf.0.png b/testing/resources/pixel/bug_1241587_expected.pdf.0.png
new file mode 100644
index 0000000..319932a
--- /dev/null
+++ b/testing/resources/pixel/bug_1241587_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1258634.in b/testing/resources/pixel/bug_1258634.in
new file mode 100644
index 0000000..4f6c778
--- /dev/null
+++ b/testing/resources/pixel/bug_1258634.in
@@ -0,0 +1,52 @@
+{{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 2 0 R
+ /Contents 4 0 R
+ /Resources <<
+ /XObject <</Im25 5 0 R>>
+ /ProcSet [/PDF /Text /ImageC /ImageI]
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+50 0 0 50 0 0 cm
+/Im25 Do
+endstream
+endobj
+{{object 5 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /Width 447
+ /BitsPerComponent 8
+ /Name /X
+ /Height 150
+ /Decode [0.0 255.0]
+ /Filter [/ASCII85Decode /FlateDecode /JPXDecode]
+ {{streamlen}}
+>>
+stream
+GhQG_Yti1j&;L4+&hHI;8ZC3n%U"m$jU!\c,9XuY,s^4`#\,T>=L,1U+n6>\!><`+M:^s'mTe/j=LRYs
+s8.3L+D^;KgeV9U!5mR3B@E<uP']`:c-mCld>-<04O@hs(t9_.OHhdR,`!m>B3Y]5fsTr<!RNbrCD*'p
+pBsV>I8B!]N0Qp5:2ZMS"Y$P]`Nj1F-AG$1/&,f>CDqEZc>b%B?K"ua*C"PE8JAWBAF'tm-j$q6qGfBk
+e[sGH+nbX3p>#O@GMK5P5JOj(TmQF;al^NZ[gDFm:K9qc8>D89+cSi-LGT>%=t\92mfZn.~>
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1258634_expected.pdf.0.png b/testing/resources/pixel/bug_1258634_expected.pdf.0.png
new file mode 100644
index 0000000..f6d66eb
--- /dev/null
+++ b/testing/resources/pixel/bug_1258634_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1258968.in b/testing/resources/pixel/bug_1258968.in
new file mode 100644
index 0000000..bb1318f
--- /dev/null
+++ b/testing/resources/pixel/bug_1258968.in
@@ -0,0 +1,211 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Contents 12 0 R
+ /MediaBox [0 0 720 540]
+ /Parent 2 0 R
+ /Resources <<
+ /Pattern <<
+ /P1 10 0 R
+ >>
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Mask
+ /BC [1.0]
+ /S /Luminosity
+>>
+endobj
+{{object 5 0}} <<
+ /Type /ExtGState
+ /AIS true
+ /BM /Normal
+ /CA 1.0
+ /OP false
+ /OPM 1
+ /SA true
+ /SMask 4 0 R
+ /ca 1.0
+ /op false
+>>
+endobj
+{{object 6 0}} <<
+ /Bounds [0.300003]
+ /Domain [0.0 1.0]
+ /Encode [0.0 1.0 0.0 1.0]
+ /FunctionType 3
+ /Functions [8 0 R 9 0 R]
+ /Range [0.0 1.0 0.0 1.0 0.0 1.0]
+>>
+endobj
+{{object 7 0}} <<
+ /Filter [/ASCII85Decode /FlateDecode]
+ /N 3
+ {{streamlen}}
+>>
+stream
+GhS^SG>qPn(5L5LGC#t-0Q\.Xmqi'h]h+J9$H('f6Q':!Tns3j'o47^;QNDg`.65@8?@VV+Wr1@%nI7.
+@PE\<W+8BL8pko0n)`[*a1^BaBCBp9fmB@!/c[/h!%oo,Q3&2"-fBc+]@!CJA*DQ/!!>@!!-8.-XR+0H
+0X#F<P-:cF4^6tMr^F#G!@3-O1A2$Oe%u&3BiZQf!!!]a@S8U,RjI''&Z=^t1<&ri&Q9?@`=/N[bSMQR
+@X0[UI:f6M5+gLsbj9Em>K#Nk1YM#/0mAP#F,l>,@O+4W,ro4*Y)%"S==[+5c5#jgEGR[kR10.R,dn(?
+XCMp4M3u3l%s8]Y&ccmHUb=^d$Pm^P"QDeZB4U/r\OjmE\fOM:DJj+-T_i\Kdl7D[EA3TAb2fnnmMY8W
+9O9m_T?mF,1?.$977FX>\$N=o[P0H9U/:l4nS_%TgX#LIjAXAI>43QA^[fQ]!.ZdOWm.t:C^IKLZu6(4
+rn\_TXo/5jMYboTs2B7sid"*2#VtdA9lkAs9t21/M7B4nl[Ltn%=Rb0'Cg7\Q\Y"F8Rr1W"E\Y>=;bfj
+69[OoPKDh2NW2,Zs#BMOo.UO5,]`Z"SgYdL1hBouP,R2!2HR]=i6f*Js4"u:)"o>*SnjH:N?80Yc3-]2
+;ZZH100iBj!/]M^meZC#$c#74n]ctkS,>DCT*5,iY&K*iOj-Vs'^$MU:1jn/3M4pAf+O_W!D!8A:]RL\
+'BfI,?ib(p")n:4^^gR!E"EIZ!Z"<2l$s:b5p9>@8jF9H#'PSZYR`RB-I;fhJ9Gl9>Qe-D%<)shJbrgh
+9Rpb`(;'hZ&-QDb1.6[-+>l\/15%2$#35o4630n2'Fi#K8IuCF6A<bjW!o\ZlC\5U'D*ku)S-2[K*J!\
+cknSKr<g5j&XW]"@22lL2ME$Q/*me%_^jXj3"%?hMrCdW%GUq*KA5=Bn-sUJ*eg6&EsP$e#RsX$N"H9R
+NW^L[80Yo@ef^e[2:@#^+Iu%6EXNaRArsJM%#TY2UPr%[-m-qo'FU1[>(\:peY/"rg/-V(>Gs:+/F"IK
+N)iCeU@$@BkU:Nc3Yu(RY!sL"T0cmS&"tAtY;$k&%ASid`V<i80e&JiQu^Cl9Io6,AcOWIRDWL=$<TZs
+,;ohN`!W^pfNRVrm!7q?FoqOF8&VCc/9"q*(`X&fKWE*5`LXmUi[/XQGU^9`hB!blBB/UR(&17G-b^Ze
+JR9Nj'6FP@KQ'6b@6L<=cpG69GX-LF,SDtjG/Oq9Qai&X6Te,V8fT(i--;5W8FE9UP*!&9FI70$FI2W_
+9IB(e?-"a2!I63+M0t0XM#9_o,%2\S/&@NR.o@tI-8?q=>S8]h/a4aq;R/Qn>4SJ"^Z:=f?:m`1?&C)=
+7]Ke[[+'o]S2<[g>WPD:?+O;;W:Vt3.2f4GNf9kE>GbNbTZe^ME=,%[Ups?*PRWjHb%8]u'gpNJqGL7j
+A^kXsIFMD&)-JKC#h_>nHJ,X1UGRY$6h@.Nd4/fWBZ%3$E[l+4EZDVAE\2<aEqAionh0JQq,7#hq'#QX
+$?QGIKiEcn$LJ02'iBhmMBRkfML-'k/']P?PoQ#j8u5--PjU!<ap-"^b,^a;.ebF=WMlhAX(86[Wa&Gi
+;5L8'W2?LoWLd%Z<hlTRWH$IgeBY*XV9@:ml"Tc.H8g7Q?!5uR?)W3;]ha&B6W[cc7Zg0@`^<IP-:9Rd
+ZV%PsF&kq..o<9CX'+b[2I;lHSZKLt]173S7oq'9B9Q*&cBPKEY-2_oDR8a54gLZc?C"oMhJI\"5!qP7
+IujS'"5(EC+PEFET\g;=Zn^V[mP,\866"SqnVeBF7Z9pYY/WKAc,O6?1m7p',*R26kGDF[0rE]"HV?Ym
+=du7U24E>[QSJ4j0/DuQs4,!%Jn7etESHXml".EVZGZ6eQ(\r5C9Q/.CPm8-od8uWlOPn^2LLu0RqZ(Y
+gN_K/[Nc21,FD?R\8$f-F0f(dGK0'Q]oB_@re15'%`T!X%S%P:QKTs[/88?Em\o*cF?JJ@l9oiRGM;LB
+lE'EtFDc&Hm%Wa]FueV=l)O3@id/iA\XT!+hsHZ_]XtlSCA/)WLkiH0WCNSYN9;tB3mU<&)I?!Ac!Ia5
+f&!I?0DM2gr-c$NN;0FJZ@&-hBDlZF<r7%:PPi>rlhhh0(^0=13Wn>+^VCk@&!gr`jXk<[!^XOd4+\LX
+lNUu*Iij7K#nEpFGnOj5=r>s,7L=t\3u[fRgb$/_r;JIbA\r"a&X!a2&PWl/OWKXMOeYdN;6-I06n:`J
+UH;qh0bH-q@ciu'0&O3e,1\f`,^+EdmY\Xlf2aB(9Nud#:,7hIG;:3[*uTPbr]N&nfta+Nn4hQ+0["KL
+m:&4g]<J?gdi:kQ-"f!\0sNKR.`jDZ8()LbF)lEo%+h%Sa*j*0PHliQ4uFb]E9^n9A6Nb0/j2Ds1f&jm
+b3R>Lc,e'Fa>D*laW/R2ce=dEbP10Ab(d3'A9S<XU7\DkTq64uE%K$YV`s9<DFEp74R"t7?>mPDrg*K(
+!j@3Z=.r["<co.W(ToD2>OB#u^`$l\_O@X8RVRi5:9=%nG@WjFIHpYNMTgr-MdC7VgsB3*;S9/WI&$GU
+jq<L`I`]YXLJ@Y(UGd0PpMI1j;S2%#.D,4uR9<[8g&#ejq=Ur8^T.*EEGORb^'^?e^"Y6qGh[t^4)[>]
+.:$(Q3PH_W7p?_AWjM'AcFBFsWi`;qm3:m0mM`26W9L82Wng_Q^+Qs6W8=YjFgpr0/\C3?YkJRoFkPNT
+:M!nd^3BIbqsPNo51%lRdnkH(F"le8PK&EuT!'#=2]YdY<-IKC@G]tj,G@W=Rr"R_N[c-QBSN"1c-s^B
+O59`Rj-u&Ej-BeL[+;PCi4/:Odr49]*BrG3U?a)bABT2NhsETr3E=c;[H4mB[O8PFG!pN_SG#&#e`kG+
+l?e``&"c]`;h%jACrJEO\`.[gk6oS]?-'Z4F"uq=GZS/%rC^"SDKgkXpKdL-I.`BKPtO9C4m0"0?9NAS
+rn?W&N9fjiCi6WQ>5+Wb_>8pcI5ZLjrO2YF2skL+%P/JW2s<AKnWq4t/%9qFh\]NX1bcRYps)FsaQOZ<
+SZ058G@0.K\[QO-h#m;%dYC^2WuicTCA:[+a4=+WdBd=(A%dr@*oA&PNBd:hqks0%`D=QNW[H=DS#.a6
+c:iRZhgE$5l,9pPB+$LA5>2$hqmkSTms]9PV0=h4G2`]*`T")_0,7e7IV&2PCG'l"dHgGV$hL35']'gT
+bBVe50(RiZ6LK3eqjJ&l+$)X_IseJf^:6`/%I9DR"T~>
+endstream
+endobj
+{{object 8 0}} <<
+ /BitsPerSample 8
+ /Decode [0.0 1.0 0.0 1.0 0.0 1.0]
+ /Domain [0.0 1.0]
+ /Encode [0.0 255.0]
+ /FunctionType 0
+ /Order 1
+ /Range [0.0 1.0 0.0 1.0 0.0 1.0]
+ /Size [256]
+ /Filter [/ASCII85Decode /FlateDecode]
+ {{streamlen}}
+>>
+stream
+GhP$l@s8#P'EukPrU7.2gU>qX[P\^,dq&..1\:=oP+ts]GDY.=PP0gGgdV+ET?;OHG1/`sLjOlq&KI(L
+f!-\HkIq7B&uVaYlSQoV!1g7^3I>]d(N>LlkRoLPlerR``AJ^1laUAaDW2FL2e\0<i3#5WL\l>3'D.A9
+M'G,`P1C:/jXGM1k<+/Y<ITd+C5-&Q2`uGFGN-<%UC[-5cTgAE^1[]9/&(8Tpb%$RVd?*)i\rOj:o!r(
+Y;jmc/hX\'%se]<CW\igDcUQPK,?@ZehBR,ZQ@)Q]b/9f-Fo9Pdsk?A?l!fgUNnD0]niMN^"bAV"XS'/
+=;>VRB!'mj@tXe1W1qj\;-c$4D6r7E%]l8Y~>
+endstream
+endobj
+{{object 9 0}} <<
+ /BitsPerSample 8
+ /Decode [0.0 1.0 0.0 1.0 0.0 1.0]
+ /Domain [0.0 1.0]
+ /Encode [0.0 255.0]
+ /FunctionType 0
+ /Order 1
+ /Range [0.0 1.0 0.0 1.0 0.0 1.0]
+ /Size [256]
+ /Filter [/ASCII85Decode /FlateDecode]
+ {{streamlen}}
+>>
+stream
+GhW&pn(7TS56iq>1%`hbnT)[V<.!'@^]o`~>
+endstream
+endobj
+{{object 10 0}} <<
+ /Type /Pattern
+ /BBox [-79.0947 -75.3227 29.2131 32.9851]
+ /Matrix [2.58008 0.0 0.0 -2.58008 0.0 540.0]
+ /PaintType 1
+ /PatternType 1
+ /Resources <<
+ /ExtGState << /GS0 5 0 R >>
+ /Shading << /Sh0 13 0 R >>
+ >>
+ /TilingType 3
+ /XStep 108.308
+ /YStep 108.308
+ {{streamlen}}
+>>
+stream
+/GS0 gs
+BX /Sh0 sh EX Q
+Q
+endstream
+endobj
+{{object 11 0}} <<
+ /Type /Pattern
+ /BBox [0.0 0.0 10.0 10.0]
+ /Matrix [0.899994 0.0 0.0 -0.899994 0.0 540.0]
+ /PaintType 1
+ /PatternType 1
+ /Resources <<
+ /ExtGState << /GS0 15 0 R >>
+ /ProcSet [/PDF /ImageC]
+ >>
+ /TilingType 3
+ /XStep 10.0
+ /YStep 10.0
+ {{streamlen}}
+>>
+stream
+/GS0 gs
+endstream
+endobj
+{{object 12 0}} <<
+ {{streamlen}}
+>>
+stream
+/P1 scn
+q 1 0 0 1 24.0804 441.1595 cm
+0 0 m
+14.956 18.738 42.588 21.551 61.719 6.282 c
+B*
+endstream
+endobj
+{{object 13 0}} <<
+ /ShadingType 4
+ /AntiAlias false
+ /BitsPerComponent 8
+ /BitsPerCoordinate 32
+ /BitsPerFlag 8
+ /ColorSpace [/ICCBased 7 0 R]
+ /Decode [-79.0947 49.8358 -75.3227 76.1016 0.0 1.0]
+ /Function 6 0 R
+ /Filter [/ASCII85Decode /FlateDecode]
+ {{streamlen}}
+>>
+stream
+GhQY8J:de0msApO^RfKC56Jf5[]KL9Ib"F1@(I<ko)G&.2NW(&f'iVl^SO3H`a9ee^Y3Q0ZIY89s8Vk4
+0+)K'[da=[+W`o"m.eU#2uc&GYr(>85FqT^,V,YdJ+(;u+D](<_>cUbetB6*(-VW2^AJb.F\q5Ps&D?-
+HS=&+rElD6$-7Elci&@,$-39'=C1p_<'1hJopO.gs'@^7+R>rd\c8R"8jKJ.V#NA6dQt1DRfB!CA-K9s
+^;S%N!W[6dO+%~>
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1258968_expected.pdf.0.png b/testing/resources/pixel/bug_1258968_expected.pdf.0.png
new file mode 100644
index 0000000..0770f77
--- /dev/null
+++ b/testing/resources/pixel/bug_1258968_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1258968_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1258968_expected_skia.pdf.0.png
new file mode 100644
index 0000000..18f4279
--- /dev/null
+++ b/testing/resources/pixel/bug_1258968_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1271578.in b/testing/resources/pixel/bug_1271578.in
new file mode 100644
index 0000000..8837f5b
--- /dev/null
+++ b/testing/resources/pixel/bug_1271578.in
@@ -0,0 +1,95 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+ /MediaBox [0 0 100 100]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /Resources <<
+ /Font <<
+ /F1 5 0 R
+ >>
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+BT
+40 30 Td
+/F1 36 Tf
+(!)Tj
+ET
+endstream
+endobj
+{{object 5 0}} <<
+ /Type /Font
+ /Subtype /TrueType
+ /BaseFont /Test
+ /FirstChar 33
+ /FontDescriptor 6 0 R
+ /LastChar 33
+ /ToUnicode 7 0 R
+ /Widths [415]
+>>
+endobj
+{{object 6 0}} <<
+ /Type /FontDescriptor
+ /Ascent 922
+ /CapHeight 1151
+ /Descent -196
+ /Flags 4
+ /FontBBox [0 -228 2000 921]
+ /FontFile2 8 0 R
+ /FontName /Test
+ /ItalicAngle 0
+ /MissingWidth 750
+ /StemV 0
+>>
+endobj
+{{object 7 0}} <<
+ {{streamlen}}
+>>
+stream
+/CIDInit /ProcSet findresource begin 12 dict begin begincmap /CIDSystemInfo <<
+/Registry (Test) /Ordering (UCS) /Supplement 0 >> def
+/CMapName /Test def
+1 begincodespacerange <21> <21> endcodespacerange
+1 beginbfrange
+<21> <21> <0629>
+endbfrange
+endcmap CMapName currentdict /CMap defineresource pop end end
+endstream
+endobj
+{{object 8 0}} <<
+ /Filter [/ASCII85Decode /FlateDecode]
+ {{streamlen}}
+ /Length1 1152
+>>
+stream
+GhV7Y6"got'R_dDS#gHM71g``Z@S[j_4%a(QV`9fkbUmea@WX0RPr-&&s@b[7(`ka2\?Q-3Jf-R,)CL5
++:'#&%pm0q>4<`1=:YRPqIXpiXEsF'?ZtVG5P+m"s7:FX!"@g9'FMfgC$OLFobW-97rRDZR3Q3#5!Kte
+`(BOQMXNDWo^*Ch6f,j"]QUX5UCu-\c8uD,Qf^N7h9H,n#J"'>H?6.=dl3,__9j/A".)HC>)PLCo/k-1
+"r["/>//RcGSl0.rOV.*\(nn.31Ne1G]h?9?>MjaN`CZpVqGa-MKe)6q>QV=&"Q?-pmAY>4Whjs`uq4c
+mV;`O*^=oD*[@G-\RA3RXTOF,W/FDeT#)as6^&HTUgHh!*X1;3AD^HB/!QKb:H@mrGO0<EmLMh\#4llE
+A';]e-+,IKG=UlFe>*J=FbK^:F'=Mm-cDb]6r/02KD#tL3CHuIn4i%?QsJjr7)5oI[)It$92\:,ce-B`
+4sGT-C-n@OG38UMfPkfOIQ-Jj@F(Y.hW2(Iq>+`)XgHXjHl9Ce9a"N+(bCO<)6AYC#4'DR1YAS5CiG&V
+?_<Lh/*K&3/;[j@/)h.n/`OYPgPCV49H9b@JeG(sUVkBV[e96!BUQ4ra!)G#ZWhGe794V,\_:dO2X50t
+2!b.710r>Oi8%b@WH:'*(#][r=j)N:=BIHST\St9pK-?]J,^S!!-4ih@f~>
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1271578_expected.pdf.0.png b/testing/resources/pixel/bug_1271578_expected.pdf.0.png
new file mode 100644
index 0000000..410bbdb
--- /dev/null
+++ b/testing/resources/pixel/bug_1271578_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1271578_expected_agg_mac.pdf.0.png b/testing/resources/pixel/bug_1271578_expected_agg_mac.pdf.0.png
new file mode 100644
index 0000000..7318ef2
--- /dev/null
+++ b/testing/resources/pixel/bug_1271578_expected_agg_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1286_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1286_expected_skia.pdf.0.png
new file mode 100644
index 0000000..b4442e4
--- /dev/null
+++ b/testing/resources/pixel/bug_1286_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1287409.pdf b/testing/resources/pixel/bug_1287409.pdf
new file mode 100644
index 0000000..3973822
--- /dev/null
+++ b/testing/resources/pixel/bug_1287409.pdf
Binary files differ
diff --git a/testing/resources/pixel/bug_1287409_expected.pdf.0.png b/testing/resources/pixel/bug_1287409_expected.pdf.0.png
new file mode 100644
index 0000000..60507e1
--- /dev/null
+++ b/testing/resources/pixel/bug_1287409_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1287409_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1287409_expected_skia.pdf.0.png
new file mode 100644
index 0000000..0b00554
--- /dev/null
+++ b/testing/resources/pixel/bug_1287409_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1288_1_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1288_1_expected_skia.pdf.0.png
new file mode 100644
index 0000000..df6b87e
--- /dev/null
+++ b/testing/resources/pixel/bug_1288_1_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1288_2_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1288_2_expected_skia.pdf.0.png
new file mode 100644
index 0000000..3a8cbee
--- /dev/null
+++ b/testing/resources/pixel/bug_1288_2_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1296_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1296_expected_skia.pdf.0.png
new file mode 100644
index 0000000..828856c
--- /dev/null
+++ b/testing/resources/pixel/bug_1296_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1304714.in b/testing/resources/pixel/bug_1304714.in
new file mode 100644
index 0000000..976d901
--- /dev/null
+++ b/testing/resources/pixel/bug_1304714.in
@@ -0,0 +1,107 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /AcroForm [4 0 R 6 0 R 8 0 R]
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Annots [4 0 R 6 0 R 8 0 R]
+ /MediaBox [0 0 200 200]
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Tx
+ /P 3 0 R
+ /AP <<
+ /N 5 0 R
+ >>
+ /F 4
+ /Ff 1
+ /Rect [20 10 180 190]
+ /T (Red bottom layer)
+>>
+endobj
+{{object 5 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /BBox [0 0 160 180]
+ {{streamlen}}
+>>
+stream
+1 0 0 rg
+0 0 160 180 re
+f
+endstream
+endobj
+{{object 6 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Tx
+ /P 3 0 R
+ /AP <<
+ /N 7 0 R
+ >>
+ /F 4
+ /Ff 1
+ /Rect [80 90 130 130]
+ /T (Green middle layer)
+>>
+endobj
+{{object 7 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /BBox [0.0 0.0 50 40]
+ {{streamlen}}
+>>
+stream
+0 1 0 rg
+0 0 50 40 re
+f
+Q
+endstream
+endobj
+{{object 8 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Tx
+ /P 3 0 R
+ /AP <<
+ /N 9 0 R
+ >>
+ /F 4
+ /Ff 1
+ /Rect [90 100 120 120]
+ /T (Blue top layer)
+>>
+endobj
+{{object 9 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /BBox [0.0 0.0 30 20]
+ {{streamlen}}
+>>
+stream
+0 0 1 rg
+0 0 30 20 re
+f
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1304714_expected.pdf.0.png b/testing/resources/pixel/bug_1304714_expected.pdf.0.png
new file mode 100644
index 0000000..58b7600
--- /dev/null
+++ b/testing/resources/pixel/bug_1304714_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1308_1_expected.pdf.0.png b/testing/resources/pixel/bug_1308_1_expected.pdf.0.png
index 44c0446..33a45c0 100644
--- a/testing/resources/pixel/bug_1308_1_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_1308_1_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1308_1_expected_win.pdf.0.png b/testing/resources/pixel/bug_1308_1_expected_win.pdf.0.png
deleted file mode 100644
index 70d0f23..0000000
--- a/testing/resources/pixel/bug_1308_1_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_1308_expected.pdf.0.png b/testing/resources/pixel/bug_1308_expected.pdf.0.png
index 1c36944..1013fb1 100644
--- a/testing/resources/pixel/bug_1308_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_1308_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1308_expected_mac.pdf.0.png b/testing/resources/pixel/bug_1308_expected_mac.pdf.0.png
deleted file mode 100644
index 570f992..0000000
--- a/testing/resources/pixel/bug_1308_expected_mac.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_1308_expected_win.pdf.0.png b/testing/resources/pixel/bug_1308_expected_win.pdf.0.png
deleted file mode 100644
index 0acd825..0000000
--- a/testing/resources/pixel/bug_1308_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_1330_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1330_expected_skia.pdf.0.png
new file mode 100644
index 0000000..0ae57c7
--- /dev/null
+++ b/testing/resources/pixel/bug_1330_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1338_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1338_expected_skia.pdf.0.png
new file mode 100644
index 0000000..7698e64
--- /dev/null
+++ b/testing/resources/pixel/bug_1338_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1355.in b/testing/resources/pixel/bug_1355.in
new file mode 100644
index 0000000..a00fcf1
--- /dev/null
+++ b/testing/resources/pixel/bug_1355.in
@@ -0,0 +1,85 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents [6 0 R]
+ /Resources 4 0 R
+ /MediaBox [0 0 200 200]
+>>
+endobj
+{{object 4 0}} <<
+ /ProcSet [/PDF /Text]
+ /Font <<
+ /F1 5 0 R
+ >>
+>>
+endobj
+{{object 5 0}} <<
+ /Type /Font
+ /Subtype /Type0
+ /BaseFont /HonMincho-M
+ /Encoding /90pv-RKSJ-H
+ /DescendantFonts [7 0 R]
+>>
+endobj
+{{object 6 0}} <<
+ {{streamlen}}
+>>
+stream
+BT
+/F1 4 Tf
+6 0 0 6 150 150 Tm
+0 0 0 1 k
+<eb5b>Tj
+-10 0 TD
+<eb69>Tj
+0 -8 TD
+<eb6a>Tj
+ET
+endstream
+endobj
+{{object 7 0}} <<
+ /Type /Font
+ /BaseFont /HonMincho-M
+ /CIDSystemInfo <<
+ /Ordering (Japan1)
+ /Registry (Adobe)
+ /Supplement 2
+ >>
+ /DW 1000
+ /FontDescriptor 8 0 R
+ /Subtype /CIDFontType2
+ /W [1 [250] 18 19 500 54 [833] 81 [583]]
+>>
+endobj
+{{object 8 0}} <<
+ /Type /FontDescriptor
+ /Ascent 880
+ /CapHeight 709
+ /Descent -120
+ /Flags 6
+ /FontBBox [-165 -221 1066 952]
+ /FontName /HonMincho-M
+ /ItalicAngle 0
+ /StemV 81
+ /Style <<
+ /Panose <010502020500000000000000>
+ >>
+ /XHeight 450
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1355_expected.pdf.0.png b/testing/resources/pixel/bug_1355_expected.pdf.0.png
new file mode 100644
index 0000000..3bd23af
--- /dev/null
+++ b/testing/resources/pixel/bug_1355_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1355_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1355_expected_skia.pdf.0.png
new file mode 100644
index 0000000..dae811b
--- /dev/null
+++ b/testing/resources/pixel/bug_1355_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1356149.in b/testing/resources/pixel/bug_1356149.in
new file mode 100644
index 0000000..6dd6502
--- /dev/null
+++ b/testing/resources/pixel/bug_1356149.in
@@ -0,0 +1,216 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /MediaBox [0 0 100 100]
+ /Resources <<
+ /ProcSet [/PDF /Text]
+ /Font <<
+ /T1_0 5 0 R
+ >>
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+BT
+/T1_0 10 Tf
+-0.02 Tc 0.02 Tw
+1 0 0 1 10 20 Tm
+[(\201)-3.6(\027\036)-5.4(\007)-6.6(\007)-5(\026)-2.5(\034\033)]TJ
+ET
+endstream
+endobj
+{{object 5 0}} <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /JXUIQN+BernhardGothic-Hvy
+ /Encoding 6 0 R
+ /FirstChar 1
+ /FontDescriptor 7 0 R
+ /LastChar 173
+ /Widths [665 576 576 625 576 255 535 616 247 0 250 529 970 540 534 529 531 542
+ 518 714 522 238 447 352 372 487 200 399 235 538 499 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 543 576 535 0 0 0 0 0 0 0 0 0 0 0 620
+ 0 635 561 0 0 0 0 0 0 0 0 0 0 0 0 542 0 0 332 0 0 0 0 0 0 0 0 0 0 0 0
+ 500]
+>>
+endobj
+{{object 6 0}} <<
+ /Type /Encoding
+ /BaseEncoding /WinAnsiEncoding
+ /Differences [1 /H /zero /two /N /one /period /o /V /comma /.notdef
+ /quotesingle /E /W /n /d /b /u /p /fi /w /e /l /c /t /r /a
+ /space /s /i /h /T 127 /g /three /S 141 /C 143 /D /B 157 /P 160
+ /f 173 /bullet]
+>>
+endobj
+{{object 7 0}} <<
+ /Type /FontDescriptor
+ /Ascent 1000
+ /CapHeight 666
+ /CharSet (/H /zero /two /N /one /period /o /V /comma /quotesingle /E /W /n /d
+ /b /u /p /fi /w /e /l /c /t /r /a /space /s /i /h /T /g /three /S /C
+ /D /B /P /f /bullet)
+ /Descent -500
+ /Flags 4
+ /FontBBox [-500 -500 1500 1000]
+ /FontFamily (Bernhard Gothic)
+ /FontFile3 8 0 R
+ /FontName /JXUIQN+BernhardGothic-Hvy
+ /FontStretch /Normal
+ /FontWeight 700
+ /ItalicAngle 0
+ /StemV 144
+ /XHeight 472
+>>
+endobj
+{{object 8 0}} <<
+ /Subtype /Type1C
+ /Filter [/ASCIIHexDecode /FlateDecode]
+ {{streamlen}}
+>>
+stream
+789c959769541467bac7ab68abab156d17ac11aab4aa1171178c1963d48c
+0b6e88a8b8a008ca8e022a48d3b2ef427757757575030aa24023c8ee8202
+c60ddce3763489a249266372332766726ecc7827b9f129eecb9c735f265f
+e6e3bda73f749fd3d5effb2cffe7f7fc9b2446b8112449b24161a1ebb76c
+9a1390604c498c31c6af4b352526c5cd0bccc81efed6a072a43a7984cd66
+97a92984c773710c788d8590719031be63f2d8f3130837927cffc399ab66
+bdb778f102c3f6c404c3dad414936155eac1433129d986f529717e730d2b
+83830d5b93f6259ad20d5b13d2138c1909f1810931f80282c42f229120c6
+10c4588218af217c34c42c82f02788f708e28f24b188240274c44682d8ec
+466cd3106104612288830439c140cc20e6127ec47c4d802690d8448410db
+c81d648cee0091e266d46412cdc43704311327488cc087fb1211442ef184
+f4218bc8bfb9e5b97da9d9a2691f9138e247ea20f5423b439bae6da359da
+49bfd0edd6758e9c3cf2e4a88051a65197dcd7ba978f2647078fee1a238c
+393be63ff46efaeca17e3d7a6773a9752e528feeaa9ed041c242d5936981
+0e0a79685b5007a547f76dae5f5c24903f68de210373a2c6e170b5eaba6e
+d6f6f4b277133ede1599941c15dd76e8aa99b7da6c56f67046943185d7a3
+1bc53083bc053334b0e731d3b3bf333636393926b6f340cfc7eded1ff3fa
+12f05057c224b2037828006f8d3a03be61901017b06c59dc000830e5d2d7
+2f5ef62e43061ebcab1988f40516f947ecc94b8de31787c4f9223f16ed79
+8b26821f2cfc193c2195d7db5c83340ed30453342a31e8cfa0590594d526
+5b5951ce510ef2ca7e7b52226ba645ba0cfd932aa5cb964b26314f2af392
+a4e1b06181a2154f4a8d52874ebc606b73b14edaa94a94de0663d525e045
+c25fc1a0f95c6d627e5e43dd69e9eeece1ce9f4a8f4f2d4b294c13920382
+9066f7fae27ccf3988a3d4955a074ca4ac8793c4744ed44e95cf5faaaabd
+f8d4ebcedb8647032c908b5e21b779013b6645f3fa23e0a5ae803f90f02d
+0efaefea1de6f54a473975b9a3b1b69d6b6f4a4f29114b2c2542ce5ee3a1
+d521668be79b8cfb3b37b091f1f1bb6362da6f24f348bb9e9adbf74de88f
+1c3040fc02e39e27df09ec16ae5caa6efe84fd61c997b357fc69d7ca045e
+5f5100132110f4e003e3c947608059e0a38118e8619c4d47ed8ddcb32b7b
+b787462705a615dbcb0f09a2ba8a71ba2ae57aee41cfa1d8a8c8031bf24b
+65255b90c08b42f3806464cb7055e5627ba6fda0e3434f9156d01794486f
+34af284be09d1295fbe8f191071cac00b7ef807d917c2ba853e86d6dba76
+8305adcf1768feb2546bd6525effa400387529f06433ccd2a89fabc0f444
+f977ace45010f247efa3cd681bf8a1f721f8bb9fcedfbe27982bf6f5e734
+992b8b4e7945ed0949dfc819967c0fa361dcabd730e25a6751c23dc1ee6c
+bb79b2d55959d3e0a5ff4b014c563f82a9e4139c7205f86a20013c18f57d
+dad17c546ee0c0fdcf0bd144e4b1f843e4915d6a73640af84a77e6984815
+3e7d547a8f83482cabd9b00b76a00f60210adbb2a1387bad5062a5647158
+2a4a913d4fc9d239f62be9d96c093de44b9be938cbfe23f1bcde88e5f20e
+3c60032ef43d98a9514d83f398a1008bd619ac6ca4cae8217f1a2b4fca90
+f6f262b24d140bd4a59267034c4097acd43bfa4799aad47ea57daa2dd7d6
+c044542f51104443af9dd283c6e6c239b070de45de86b920819f4645837e
+0c5aacc53189ac5c2217c8d9ba4c39af982da5511cb243245d41dbe55a7b
+072fd7c9c7ed4775b2cde610d0222d1e530b2b9be5623957673f2ce795b2
+39f4d0329c84b8564ac1b1157ad96c36c9ccc342ad1d7fb0f3e275e90655
+41c348b0cdc2f3532a658a49bc94226648f93adbf061b0f45f4fcabc784c
+aa954e8b4f3d155a5d4d2bf439c74d471daf7f86679375611a18a018cf7a
+df60005346cb5becc19499b60fed938ba503522c65068383b6dea72c0f6d
+c7ebd9725a863075d2d024aadc26490a5f2d9d96ba79e933ca49cbea24aa
+92969fcbe76c0d72b917be5a61d12c91c29590582b2e4406af2c1f3e1a65
+0d79a834a52fc4d59b83db22c244f231cc80e55872b04f3dcb381b2bb0ce
+07ae47042c0bdfbd24a75476e6086821fcc0c8a5d4b13067dc7e16f7cc9d
+2ea38b715d0ef0e27e5b9654aa33831f8d2f9559a9523a2135880f3c71b0
+f7a82aa77494fbe46675c365e155f79dbffdc4c25434e20d5ab424d5629c
+cdebb30a30f2d661e4f5c14c588869a2dad5d7cc27cb57b47ec4a1f133e6
+a251de8f83607694f02ca3293b894d8a3f101dbee3745f30bf783ab5fec5
+b7897fe1c0ebcd6b18db59da56d02c18afed699fdd51e8f05cd598d27481
+bd78e674df8d4b99117dbc7e3be679394c21610d9e72b5188b64280719ca
+b5caf5069ceb51ed1374517b84368991d675bc75b7b85fcad15960312d39
+ca5e5a7e93ef7b3969e8fc8e56b497ac4d92c3562d7ae91ffede3ef5b7c1
+090cf2be265973e333b68816340235a271d02d3a73bb727b2505cd89b69b
+5dd16d9b6533d887ae8053bd2a2b8d7dc75b2b4e2a0e2f9b6cb397f37af5
+8ff8305f17f93f3b077906f15791ff902f0a56bdd1e6210f345dd5a319f1
+d0f9cff770eb59081f3240d7e087943eb0e01fe0fdfac66f640b1e011f98
+a481bbb080893b9812179f74f642d799ce0b3d1d076279e4842ee6dc2caa
+f1d629e74dee6e8b69478550179b569ec84d5fb6d67ff6972bde5ceda969
+3d23d4ed068fceccaea2dbd2632f3c49d4597353a18bbf7c6855c79fb82d
+9b8b8c6b04bdb100c64229c6ea661779135f1a02ac0636c12ba6b6a3c6d1
+c17d7a39724d40f8ceb529058e5a4c4c08a72a609041f3d27fdf38b2552e
+b267ebec26b9c08c15543eb49a92e81831ab348d8fbcf355da0b0ee6fef7
+2f58919cff2f687474be35374c704006a5371460484b308eac87c91a75b9
+ba943917b5b1610b87a6181081783405084482d7ad87aeae5bc2a6b754da
+3e634932b76ee7cdcf9ef5dd7c72ee6c5e5ab3a0cf2c80f138740f0875e1
+73a6c2c6e1d0ff0e9399ccb47c0b7e3aeadaf3671f5f7f7ca6c69cd122cc
+c7dace9773e52c5e4eb71d29c1d555fb701e26ca422be19412291b8fb056
+5a0ea04e44e7d9e338346e1a72435e681a68a6c2cc81db7537be151c0a55
+5c6f3e738bb5d38aea4e3968e50bc7234abf3f1ff3e238b893f0015c606e
+357434f571fd1753c24f08ed680e9399baaf288e0b0e6fbd9e25c0b73255
+de2657b5b020f8ff2762d174a4c1e97a23ef77de30f5c5d7a7bbfbb19f30
+6259b7e045fc04788d537d8f910d9f48148aa625b31821c6486514ccd65a
+6b6de552934e6ab0553b5905d340e165488291b20c247297a9e71849e7eb
+9a9a9b9a0bb24ef2576024752e34f1782c977aa834df246cda971e8c2694
+d9cacab04dd017a1ede0fe14c640094cc075e4612b081a780bdf33682b6d
+4cca2b4be44276f73f1ce8bf31d0566dcd3f2dc0fc01e6f2aaa5a7f10a9b
+8d081f9c85e7db69e071b1d651d72b6c908cc6f55c6874d5e908210269a8
+d4fb7fce7dcec1b4e72f618ee0901c8e7ff12e9952e8fb158f8ed5f2fa5c
+8cacbfc224d8e822af8337e605ded851836b19b428156b6c98f847ec0572
+8e4ed9604fb2b2c318e5b1c6443a473a2ca5f252a6942b15e94498435b1a
+ac772e6037037be9dab34d156d5c7fa3718b60a525e48b5d90159ba0581e
+ff26bf8cc53e482ae3c14fd1e2c93fefc2114112b691ed96969253fcbd8c
+e09eb5dcce5c8b69bdf02b2ee319a5b7fc0a6ecb73dc963c08247bb0d3bb
+a8c6338ea1780a046db51a4fc9c03157a22fed898c8fd91dd11d7de56a77
+ef75fc8bcf300078177915f7b117db34b94c0e561660bda1ffc22b689198
+644ee261a6432bb6494f1fb0325d0e6f30f21d3f295fe13734df44e9f371
+69be86b178c17ac302bc4eba551fe65846e5ae70161f1284056db2e1e479
+b35428e5f056c02bebb8e4909a75d239a9a61a9fa8403a75b2fdb8d2ce3d
+3895b24428a3d130e18ba41cc9385c895ca94457063369a94a6aedc64550
+7063e653e5f4515be551b641aa976af8ef8b97f5233db7687566989f300c
+f67160c5aba51ea6401a4e4b1d0385ccfad02d9bb686df7efc59ff9dc70f
+2e87ade5815718988ce7974493f12c5348870c68caafd3b0b47c40036ee0
+cdeb0b4b602cd20ea73791bc8b5d70083e0dc2c09b91611b55d5d6e068e4
+9e5e8d58b72e223c2835bdb2de24a0f97014275f11138735e0c0cbd24cc7
+d25952a1f9f7bdec8b511c421575d558cf73c0bdfd070e904704b6f11f2d
+4addb55ad0a3205ccb077859dc012f0d04aa7b19643051d5bb1d9bd03c36
+48dc2a86f345e04b1f819d94c3e674b02f3b2edf3c75cc92d7c077c354aa
+ea708992c2ed4accdab3171fe55f80b3680237f20cf09f02a751ddd58f98
+0b5bf7d486723e4bd62c58752bf467935067a19acaaa0bb3d8cc9cc2d4d8
+9893adc9fc763f2aa6ff5ee63deed7af3f7d7de7e0951dcd427ad5e1cabd
+55ba4395c59527d8fafa93a7da2f651feac2da892c05bdbacd457e3a1cee
+80ba9a413abbfc127b930af9d871d65e6babacc41842067998c3128b9d6a
+899c2fc77896d2d23a2a582ba1118f44096da5acf4cacf37bdeaedade9e8
+e5f7fd48e51db048995c5266cb15018fe1090537438bbdbfc9827df3808b
+ec065f0c6b4ce434358e41be5a7b96dd64648fd0324aa7d2d212f3f67351
+b9cd0382030be5031c4bb9adba9e95ebf09fcc2a1d9a61d6ca264a2eb665
+e7b2221ebacd784ae32b228fa6f121aefb7b9f71575b2acfdf152a1c2776
+c21fdaa8c20bc50f7b70067658441da36be526b99d975df65ab95aa707bc
+39d5401739b813d7158dbe3a1d19d158c8441e28632ea4226e0f240f6dc5
+c430832712c1a4eea0f4a160844c1226438606ec6a28b3312c6c63707fd8
+c3477dfd8f1eeeea0f16f4fbfe8daa10a52e6006feef88dc24fdbf185c8e
+ee0d93a14a5d40e9b3ebd47845390e1b6acb152dcab8dcaad0e0310a7877
+7560a2dacf8cfa5fd09b4ba2
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1356149_expected.pdf.0.png b/testing/resources/pixel/bug_1356149_expected.pdf.0.png
new file mode 100644
index 0000000..93827a8
--- /dev/null
+++ b/testing/resources/pixel/bug_1356149_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1372651.evt b/testing/resources/pixel/bug_1372651.evt
new file mode 100644
index 0000000..eb762eb
--- /dev/null
+++ b/testing/resources/pixel/bug_1372651.evt
@@ -0,0 +1,4 @@
+# Open the dropdown
+mousemove,140,145
+mousedown,left,140,145
+mouseup,left,140,145
diff --git a/testing/resources/pixel/bug_1372651.in b/testing/resources/pixel/bug_1372651.in
new file mode 100644
index 0000000..95b1948
--- /dev/null
+++ b/testing/resources/pixel/bug_1372651.in
@@ -0,0 +1,71 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /AcroForm <<
+ /DA (/Helv 0 Tf 0 g)
+ /DR <<
+ /Font <<
+ /Helv 4 0 R
+ >>
+ >>
+ /Fields [5 0 R 6 0 R]
+ >>
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Annots [5 0 R 6 0 R]
+ /MediaBox [0 0 200 200]
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Helvetica
+>>
+endobj
+{{object 5 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Ch
+ /DV (Item3)
+ /F 4
+ /Ff 131072
+ /I [2]
+ /MK <<
+ /BG [1.0]
+ >>
+ /Opt [(Item1) (Item2) (Item3)]
+ /P 3 0 R
+ /Rect [70 135 150 155]
+ /T (Dropdown)
+ /V (Item3)
+>>
+endobj
+{{object 6 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Btn
+ /F 4
+ /Ff 65536
+ /MK <<
+ /BG [0.5]
+ >>
+ /P 3 0 R
+ /Rect [50 90 150 110]
+ /T (Button)
+ /TU (Button Tooltip)
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1372651_expected.pdf.0.png b/testing/resources/pixel/bug_1372651_expected.pdf.0.png
new file mode 100644
index 0000000..c7386a1
--- /dev/null
+++ b/testing/resources/pixel/bug_1372651_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1372651_expected_mac.pdf.0.png b/testing/resources/pixel/bug_1372651_expected_mac.pdf.0.png
new file mode 100644
index 0000000..a749d63
--- /dev/null
+++ b/testing/resources/pixel/bug_1372651_expected_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1372651_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1372651_expected_skia.pdf.0.png
new file mode 100644
index 0000000..f1823f3
--- /dev/null
+++ b/testing/resources/pixel/bug_1372651_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1383708.in b/testing/resources/pixel/bug_1383708.in
new file mode 100644
index 0000000..77114f6
--- /dev/null
+++ b/testing/resources/pixel/bug_1383708.in
@@ -0,0 +1,100 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /MediaBox [0 0 200 200]
+ /Resources 5 0 R
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+100 0 0 20 50 50 cm
+/I1 Do
+Q
+q
+80 0 0 20 50 100 cm
+/I2 Do
+Q
+endstream
+endobj
+{{object 5 0}} <<
+ /ProcSet [/PDF /ImageB /ImageC /ImageI]
+ /XObject <<
+ /I1 6 0 R
+ /I2 8 0 R
+ >>
+>>
+endobj
+{{object 6 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 1
+ /ColorSpace [/Indexed /DeviceRGB 1 7 0 R]
+ /Filter [/ASCIIHexDecode /FlateDecode]
+ /Height 5
+ /Width 177
+ {{streamlen}}
+>>
+stream
+789cfbff1f1b6860c00eb02afedf80431887215c0094ac4349
+endstream
+endobj
+{{object 7 0}} <<
+ /Filter [/ASCIIHexDecode /FlateDecode]
+ {{streamlen}}
+>>
+stream
+789cbb70e5c6ffffffb90017e80584
+endstream
+endobj
+{{object 8 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 8
+ /ColorSpace [/Indexed /DeviceRGB 39 9 0 R]
+ /Filter [/ASCIIHexDecode /FlateDecode]
+ /Height 15
+ /Width 58
+ {{streamlen}}
+>>
+stream
+789c9d928b12822010454d7c5510a2166a61befdff3f0c310151a7c93bb0
+ecec70b82c03845c411405f09086b21c0e814e68dba17384ecdf5cfda264
+592a935a818d9b0196b9cd3e39174cd8470830847c9d532ebae74c7ed73c
+a514304ad37ce539c6694e984126711c3f6f3c243ffb945591e49de77997
+2b0f5d0e8dedca77e93925f82185b75f65c793bcce5c808df14564ddf083
+1b9ed57d146062a9d6a418f2f27a0ba03d49b560975cb621442a9cd47e51
+1475822ba236e8e75beab08d8ff4bf3e7d492308
+endstream
+endobj
+{{object 9 0}} <<
+ /Filter [/ASCIIHexDecode /FlateDecode]
+ {{streamlen}}
+>>
+stream
+789c01780087ff2c3339ffcc00ffffcca97600ebd17fe7c855ffdd48cc99
+00f6e9bfffd31dd4a92398873dffffffffee93655e3eb68300ebd067d8bf
+7fdda900ffe361be8b00ffcf0dffd831ffe87ae3bd3dfff3aaddc37fbe91
+18d9a90f333333d5a100fff8beffffddae7b00c89400e7cd7feab600deb2
+24d4a000dba70092a54914
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1383708_expected.pdf.0.png b/testing/resources/pixel/bug_1383708_expected.pdf.0.png
new file mode 100644
index 0000000..800eedc
--- /dev/null
+++ b/testing/resources/pixel/bug_1383708_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1383708_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1383708_expected_skia.pdf.0.png
new file mode 100644
index 0000000..5487865
--- /dev/null
+++ b/testing/resources/pixel/bug_1383708_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1388_2_expected.pdf.0.png b/testing/resources/pixel/bug_1388_2_expected.pdf.0.png
index 422a7e2..347f3e0 100644
--- a/testing/resources/pixel/bug_1388_2_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_1388_2_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1388_2_expected_agg_mac.pdf.0.png b/testing/resources/pixel/bug_1388_2_expected_agg_mac.pdf.0.png
new file mode 100644
index 0000000..2293e85
--- /dev/null
+++ b/testing/resources/pixel/bug_1388_2_expected_agg_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1388_2_expected_mac.pdf.0.png b/testing/resources/pixel/bug_1388_2_expected_mac.pdf.0.png
deleted file mode 100644
index b1a4843..0000000
--- a/testing/resources/pixel/bug_1388_2_expected_mac.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_1388_2_expected_win.pdf.0.png b/testing/resources/pixel/bug_1388_2_expected_win.pdf.0.png
deleted file mode 100644
index 58a14f4..0000000
--- a/testing/resources/pixel/bug_1388_2_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_1388_3_expected_agg_mac.pdf.0.png b/testing/resources/pixel/bug_1388_3_expected_agg_mac.pdf.0.png
new file mode 100644
index 0000000..3727440
--- /dev/null
+++ b/testing/resources/pixel/bug_1388_3_expected_agg_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1388_3_expected_mac.pdf.0.png b/testing/resources/pixel/bug_1388_3_expected_mac.pdf.0.png
deleted file mode 100644
index bb97a96..0000000
--- a/testing/resources/pixel/bug_1388_3_expected_mac.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_1388_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1388_expected_skia.pdf.0.png
new file mode 100644
index 0000000..eb348c8
--- /dev/null
+++ b/testing/resources/pixel/bug_1388_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1395648.in b/testing/resources/pixel/bug_1395648.in
new file mode 100644
index 0000000..6c38fb1
--- /dev/null
+++ b/testing/resources/pixel/bug_1395648.in
@@ -0,0 +1,67 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents [4 0 R]
+ /MediaBox [0 0 80 80]
+ /Resources <<
+ /XObject <<
+ /Mask 5 0 R
+ >>
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+40 5 0 60 10 10 cm
+/Mask Do
+Q
+endstream
+endobj
+{{object 5 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 8
+ /ColorSpace /DeviceRGB
+ /Filter [/ASCIIHexDecode /FlateDecode]
+ /Height 50
+ /SMask 6 0 R
+ /Width 50
+ {{streamlen}}
+>>
+stream
+789cedc2310d00000c03a07f2aaab3ea7bcf03842655555555555555f5bf01cc7818dc
+endstream
+endobj
+{{object 6 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 8
+ /ColorSpace /DeviceGray
+ /Height 50
+ /Matte [0.0 0.2 1]
+ /Width 50
+ {{streamlen}}
+>>
+stream
+789cabaa1a05a3808e4061148c027a028351300ae809246c128a027e0020065c9c90d
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1395648_expected.pdf.0.png b/testing/resources/pixel/bug_1395648_expected.pdf.0.png
new file mode 100644
index 0000000..4aca65e
--- /dev/null
+++ b/testing/resources/pixel/bug_1395648_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1395648_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1395648_expected_skia.pdf.0.png
new file mode 100644
index 0000000..80a944b
--- /dev/null
+++ b/testing/resources/pixel/bug_1395648_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1396266.in b/testing/resources/pixel/bug_1396266.in
new file mode 100644
index 0000000..fdeaf43
--- /dev/null
+++ b/testing/resources/pixel/bug_1396266.in
@@ -0,0 +1,98 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+
+% A page with scaled copies of a masked image. Any scaled image with an area
+% less than 8 times the stencil mask's original area (64x64 * 8 = 32,768) forces
+% bilinear interpolation of the stencil mask, which triggers the bug.
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /MediaBox [0 0 432 288]
+ /Resources <<
+ /XObject <<
+ /Masked 5 0 R
+ >>
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+
+% 32x32 image has an area of 1,024, so it is interpolated.
+q
+ 32 0 0 32 64 240 cm
+ /Masked Do
+Q
+
+% 64x64 image has an area of 4,096, so it is interpolated.
+q
+ 64 0 0 64 48 160 cm
+ /Masked Do
+Q
+
+% 128x128 image has an area of 16,384, so it is interpolated.
+q
+ 128 0 0 128 16 16 cm
+ /Masked Do
+Q
+
+% 256x256 image has an area of 65,536, so it is not interpolated.
+q
+ 256 0 0 256 160 16 cm
+ /Masked Do
+Q
+
+endstream
+endobj
+
+% A 3x3 base image with a stencil mask.
+{{object 5 0}} <<
+ /Subtype /Image
+ /BitsPerComponent 8
+ /ColorSpace /DeviceRGB
+ /Filter /ASCIIHexDecode
+ /Height 3
+ /Mask 6 0 R
+ /Width 3
+ {{streamlen}}
+>>
+stream
+B8C3E9 7793DB C3CEEF
+8CADF2 1B74E8 BFD5FB
+C2D3FA 9FBDF8 D9E5FC
+endstream
+endobj
+
+% A 64x64 stencil mask with horizontal and vertical lines.
+{{object 6 0}} <<
+ /Subtype /Image
+ /BitsPerComponent 1
+ /Filter [/ASCIIHexDecode /FlateDecode]
+ /Height 64
+ /ImageMask true
+ /Width 64
+ {{streamlen}}
+>>
+stream
+78DA63608080FAFF10E000E5C3687FA8388CF6818A63D0500330E802A83C41BA1EAA9E105D03554F
+36CD0C35876CFA0FD41CDAD3F2FF694D03002512CA0A
+endstream
+endobj
+
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1396266_expected.pdf.0.png b/testing/resources/pixel/bug_1396266_expected.pdf.0.png
new file mode 100644
index 0000000..6cacddf
--- /dev/null
+++ b/testing/resources/pixel/bug_1396266_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1396266_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1396266_expected_skia.pdf.0.png
new file mode 100644
index 0000000..e872c2c
--- /dev/null
+++ b/testing/resources/pixel/bug_1396266_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1402_expected.pdf.0.png b/testing/resources/pixel/bug_1402_expected.pdf.0.png
index cf913bd..751d63f 100644
--- a/testing/resources/pixel/bug_1402_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_1402_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1402_expected_mac.pdf.0.png b/testing/resources/pixel/bug_1402_expected_mac.pdf.0.png
deleted file mode 100644
index 9602820..0000000
--- a/testing/resources/pixel/bug_1402_expected_mac.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_1402_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1402_expected_skia.pdf.0.png
new file mode 100644
index 0000000..f34b78e
--- /dev/null
+++ b/testing/resources/pixel/bug_1402_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1430333.in b/testing/resources/pixel/bug_1430333.in
new file mode 100644
index 0000000..e715f8f
--- /dev/null
+++ b/testing/resources/pixel/bug_1430333.in
@@ -0,0 +1,44 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /MediaBox [0 0 200 200]
+ /Resources <<
+ /ExtGState <<
+ /A3 <<
+ /Type /ExtGState
+ /CA 0.5
+ /ca 0.5
+ >>
+ >>
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+/A3 gs
+-100 10 m
+140 20 l
+b
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1430333_expected.pdf.0.png b/testing/resources/pixel/bug_1430333_expected.pdf.0.png
new file mode 100644
index 0000000..b9fc3e9
--- /dev/null
+++ b/testing/resources/pixel/bug_1430333_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1430333_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1430333_expected_skia.pdf.0.png
new file mode 100644
index 0000000..e6a1827
--- /dev/null
+++ b/testing/resources/pixel/bug_1430333_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1469.in b/testing/resources/pixel/bug_1469.in
new file mode 100644
index 0000000..8eb970d
--- /dev/null
+++ b/testing/resources/pixel/bug_1469.in
@@ -0,0 +1,98 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /MediaBox [0 0 300 100]
+ /Resources <<
+ /XObject <<
+ % All 3 images are BGRA with the same data but interpreted differently.
+ /ImNoSMaskInData 5 0 R
+ /ImSMaskInData0 6 0 R
+ /ImSMaskInData1 7 0 R
+ >>
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+0 1 0 rg
+0 0 300 100 re f
+Q
+q
+64 0 0 64 0 0 cm
+/ImNoSMaskInData Do
+Q
+q
+64 0 0 64 100 0 cm
+/ImSMaskInData0 Do
+Q
+q
+64 0 0 64 200 0 cm
+/ImSMaskInData1 Do
+Q
+endstream
+endobj
+{{object 5 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 8
+ /ColorSpace /DeviceRGB
+ /Filter /JPXDecode
+ /Height 64
+ % No /SMaskInData here.
+ /Width 64
+ {{streamlen}}
+>>
+stream
+{{include ../bug_1469.jp2}}
+endstream
+endobj
+{{object 6 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 8
+ /ColorSpace /DeviceRGB
+ /Filter /JPXDecode
+ /Height 64
+ /SMaskInData 0
+ /Width 64
+ {{streamlen}}
+>>
+stream
+{{include ../bug_1469.jp2}}
+endstream
+endobj
+{{object 7 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 8
+ /ColorSpace /DeviceRGB
+ /Filter /JPXDecode
+ /Height 64
+ /SMaskInData 1
+ /Width 64
+ {{streamlen}}
+>>
+stream
+{{include ../bug_1469.jp2}}
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1469_expected.pdf.0.png b/testing/resources/pixel/bug_1469_expected.pdf.0.png
new file mode 100644
index 0000000..a87b79d
--- /dev/null
+++ b/testing/resources/pixel/bug_1469_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1491.in b/testing/resources/pixel/bug_1491.in
new file mode 100644
index 0000000..b973b2b
--- /dev/null
+++ b/testing/resources/pixel/bug_1491.in
@@ -0,0 +1,44 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /OpenAction 6 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /MediaBox [ 0 0 200 200 ]
+ /Count 1
+ /Kids [ 3 0 R ]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Pages
+ /Parent 2 0 R
+ /Count 1
+ /Kids [ 4 0 R ]
+>>
+{{object 4 0}} <<
+ /Type /Page
+ /Parent 3 0 R
+ /Contents 5 0 R
+>>
+endobj
+{{object 5 0}} <<
+ {{streamlen}}
+>>
+stream
+0.5 0.5 0.5 rg
+50 50 100 100 re B
+endstream
+endobj
+{{object 6 0}} <<
+ /Type /Action
+ /S /GoTo
+ /D [ 3 0 R /Fit ]
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1491_expected.pdf.0.png b/testing/resources/pixel/bug_1491_expected.pdf.0.png
new file mode 100644
index 0000000..21e9375
--- /dev/null
+++ b/testing/resources/pixel/bug_1491_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1491_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1491_expected_skia.pdf.0.png
new file mode 100644
index 0000000..69c5f08
--- /dev/null
+++ b/testing/resources/pixel/bug_1491_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1519.in b/testing/resources/pixel/bug_1519.in
new file mode 100644
index 0000000..d681156
--- /dev/null
+++ b/testing/resources/pixel/bug_1519.in
@@ -0,0 +1,48 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /MediaBox [0 0 400 400]
+ /Resources <<
+ /ExtGState <<
+ /GS1 <<
+ /ca 0.5
+ >>
+ >>
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+/GS1 gs
+0 0 1 rg
+0 0 m
+130 130 l
+150 150 180 180 200 200 c
+400 400 l
+300 300 l
+400 400 l
+300 300 l
+f
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1519_expected.pdf.0.png b/testing/resources/pixel/bug_1519_expected.pdf.0.png
new file mode 100644
index 0000000..4a6cc19
--- /dev/null
+++ b/testing/resources/pixel/bug_1519_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1546.in b/testing/resources/pixel/bug_1546.in
new file mode 100644
index 0000000..fb423d0
--- /dev/null
+++ b/testing/resources/pixel/bug_1546.in
@@ -0,0 +1,77 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Resources <<
+ /Shading <<
+ /S0 5 0 R
+ >>
+ >>
+ /Contents 4 0 R
+ /MediaBox [0 0 200 300]
+>>
+endobj
+{{object 4 0}} <<
+ {streamlen}}
+>>
+stream
+/S0 sh
+endstream
+endobj
+{{object 5 0}} <<
+ /ShadingType 4
+ /BitsPerComponent 8
+ /BitsPerCoordinate 16
+ /BitsPerFlag 8
+ /ColorSpace /DeviceRGB
+ /Decode [-32768 32767 -32768 32767 0 1]
+ /Filter /ASCIIHexDecode
+ /Function 6 0 R
+ {{streamlen}}
+>>
+stream
+00800a800a0000800a80c8ff00806480c800
+endstream
+endobj
+{{object 6 0}} <<
+ /FunctionType 3
+ /Bounds [0.5]
+ /Domain [0 1]
+ /Encode [0 1 0 1]
+ /Functions [7 0 R 8 0 R]
+ /Range [0 1 0 1 0 1]
+>>
+endobj
+{{object 7 0}} <<
+ /FunctionType 2
+ /C0 [1 0 0]
+ /C1 [0 1 0]
+ /Domain [0 1]
+ /N 1
+ /Range [0 1 0 1 0 1]
+>>
+endobj
+{{object 8 0}} <<
+ /FunctionType 2
+ /C0 [0 1 0]
+ /C1 [0 0 1]
+ /Domain [0 1]
+ /N 1
+ /Range [0 1 0 1 0 1]
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1546_expected.pdf.0.png b/testing/resources/pixel/bug_1546_expected.pdf.0.png
new file mode 100644
index 0000000..e993bf1
--- /dev/null
+++ b/testing/resources/pixel/bug_1546_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1571.in b/testing/resources/pixel/bug_1571.in
new file mode 100644
index 0000000..b4a94e1
--- /dev/null
+++ b/testing/resources/pixel/bug_1571.in
@@ -0,0 +1,112 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 200 200]
+ /Contents 4 0 R
+ /Resources <<
+ /ProcSet [/PDF /ImageB /ImageC /ImageI]
+ /ExtGState <<
+ /GS1 5 0 R
+ /GS3 6 0 R
+ >>
+ /Pattern <<
+ /Pa2 7 0 R
+ >>
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+1 0 0 1 50 100 cm
+1 1 1 RG
+/GS3 gs
+/Pattern cs
+/Pa2 scn
+/GS1 gs
+-7 7 m
+-7 -7 l
+-7 -7 l
+7 -7 l
+7 -7 l
+7 7 l
+7 7 l
+-7 7 l
+h
+B*
+Q
+q
+1 0 0 1 150 100 cm
+/Pattern cs
+/Pa2 scn
+/GS1 gs
+-7 7 m
+-7 -7 l
+-7 -7 l
+7 -7 l
+7 -7 l
+7 7 l
+7 7 l
+-7 7 l
+h
+B*
+Q
+endstream
+endobj
+{{object 5 0}} <<
+ /Type /ExtGState
+ /ca 1
+>>
+endobj
+{{object 6 0}} <<
+ /Type /ExtGState
+ /CA 0
+>>
+endobj
+{{object 7 0}} <<
+ /Type /Pattern
+ /PaintType 1
+ /PatternType 1
+ /TilingType 2
+ /BBox [0 0 0.25 0.25]
+ /Matrix [1.414 -1.414 1.414 1.414 -95.043 -381.294]
+ /PaintType 1
+ /Resources <<
+ % Acrobat requires this dictionary, even if it is empty.
+ >>
+ /XStep 0.25
+ /YStep 0.25
+ {{streamlen}}
+>>
+stream
+/DeviceRGB CS 0.941 0 0 SC
+/DeviceRGB cs 1 1 1 sc
+0 0 0.25 0.25 re
+f
+0.094 w
+0 0 m
+0.25 0 l
+0.094 w
+0.125 0 m
+0.125 0.25 l
+S
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1571_expected.pdf.0.png b/testing/resources/pixel/bug_1571_expected.pdf.0.png
new file mode 100644
index 0000000..e3d54b8
--- /dev/null
+++ b/testing/resources/pixel/bug_1571_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1638.in b/testing/resources/pixel/bug_1638.in
new file mode 100644
index 0000000..f6dea38
--- /dev/null
+++ b/testing/resources/pixel/bug_1638.in
@@ -0,0 +1,57 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /MediaBox [0 0 400 400]
+ /Resources <<
+ /ExtGState <<
+ /GS1 <<
+ /ca 1.0
+ >>
+ >>
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+/GS1 gs
+0 0 1 rg
+% The 1st sub path.
+50 350 m
+200 320 l
+50 350 l
+% The 2nd sub path.
+100 300 m
+50 300 l
+200 300 l
+300 200 l
+270 230 l
+% The 3rd sub path.
+200 50 m
+350 50 l
+200 50 l
+200 100 l
+200 50 l
+f
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1638_expected.pdf.0.png b/testing/resources/pixel/bug_1638_expected.pdf.0.png
new file mode 100644
index 0000000..23a39d5
--- /dev/null
+++ b/testing/resources/pixel/bug_1638_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1638_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1638_expected_skia.pdf.0.png
new file mode 100644
index 0000000..7ef152f
--- /dev/null
+++ b/testing/resources/pixel/bug_1638_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1639_1.in b/testing/resources/pixel/bug_1639_1.in
new file mode 100644
index 0000000..f283af7
--- /dev/null
+++ b/testing/resources/pixel/bug_1639_1.in
@@ -0,0 +1,50 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /MediaBox [0 0 200 200]
+ /Resources <<
+ /ExtGState <<
+ /GS1 <<
+ /ca 1.0
+ >>
+ >>
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+/GS1 gs
+0 0 1 rg
+% A horizontal path.
+20 180 m
+180 180 l
+% A vertical path.
+50 160 m
+50 100 l
+% A diagonal path.
+20 50 m
+180 100 l
+f
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1639_1_expected.pdf.0.png b/testing/resources/pixel/bug_1639_1_expected.pdf.0.png
new file mode 100644
index 0000000..2734462
--- /dev/null
+++ b/testing/resources/pixel/bug_1639_1_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1639_1_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1639_1_expected_skia.pdf.0.png
new file mode 100644
index 0000000..fa53f12
--- /dev/null
+++ b/testing/resources/pixel/bug_1639_1_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1693.in b/testing/resources/pixel/bug_1693.in
new file mode 100644
index 0000000..e59b5f8
--- /dev/null
+++ b/testing/resources/pixel/bug_1693.in
@@ -0,0 +1,101 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 200 200]
+ /Resources <<
+ /ProcSet [/PDF /ImageC]
+ /ExtGState <<
+ /G3 4 0 R
+ /G8 5 0 R
+ >>
+ /Pattern <<
+ /P7 6 0 R
+ >>
+ >>
+ /Contents 7 0 R
+>>
+endobj
+{{object 4 0}} <<
+ /BM /Normal
+ /ca 1
+>>
+endobj
+{{object 5 0}} <<
+ /BM /Normal
+ /ca .5
+>>
+endobj
+{{object 6 0}} <<
+ /Type /Pattern
+ /PaintType 1
+ /PatternType 1
+ /TilingType 1
+ /BBox [0 0 400 400]
+ /Matrix [256 0 0 32 0 0]
+ /XStep 512
+ /YStep 512
+ /Resources <<
+ /ProcSet [/PDF /ImageC]
+ /ExtGState <<
+ /G3 4 0 R
+ >>
+ /XObject <<
+ /X4 8 0 R
+ >>
+ >>
+ {{streamlen}}
+>>
+stream
+q
+1 0 0 -8 0 8 cm
+/G3 gs
+/X4 Do
+Q
+endstream
+endobj
+{{object 7 0}} <<
+ {{streamlen}}
+>>
+stream
+1 1 1 RG
+1 1 1 rg
+/G3 gs
+/Pattern CS
+/Pattern cs
+/P7 SCN
+/P7 scn
+/G8 gs
+0 0 200 150 re
+f
+endstream
+endobj
+{{object 8 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /Width 50
+ /Height 50
+ /BitsPerComponent 8
+ /ColorSpace /DeviceRGB
+ /Filter [/ASCIIHexDecode /FlateDecode]
+ {{streamlen}}
+>>
+stream
+789cedc2310d00000c03a07f2aaab3ea7bcf03842655555555555555f5bf01cc7818dc
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1693_expected.pdf.0.png b/testing/resources/pixel/bug_1693_expected.pdf.0.png
new file mode 100644
index 0000000..d65bd0d
--- /dev/null
+++ b/testing/resources/pixel/bug_1693_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1723.in b/testing/resources/pixel/bug_1723.in
new file mode 100644
index 0000000..aac04d2
--- /dev/null
+++ b/testing/resources/pixel/bug_1723.in
@@ -0,0 +1,61 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+ /MediaBox [0 0 300 400]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents [4 0 R]
+ /Resources <<
+ /ColorSpace <<
+ /DefaultCMYK /DeviceRGB
+ >>
+ /XObject <<
+ /Img 5 0 R
+ >>
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+200 0 0 300 50 50 cm
+/Img Do
+endstream
+endobj
+{{object 5 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 8
+ /ColorSpace /DeviceCMYK
+ /Filter /ASCIIHexDecode
+ /Height 8
+ /Width 8
+ {{streamlen}}
+>>
+stream
+FF000000 FF000000 FF000000 FF000000 00FF0000 00FF0000 00FF0000 00FF0000
+FF000000 FF000000 FF000000 FF000000 00FF0000 00FF0000 00FF0000 00FF0000
+FF000000 FF000000 FF000000 FF000000 00FF0000 00FF0000 00FF0000 00FF0000
+FF000000 FF000000 FF000000 FF000000 00FF0000 00FF0000 00FF0000 00FF0000
+
+0000FF00 0000FF00 0000FF00 0000FF00 000000FF 000000FF 000000FF 000000FF
+0000FF00 0000FF00 0000FF00 0000FF00 000000FF 000000FF 000000FF 000000FF
+0000FF00 0000FF00 0000FF00 0000FF00 000000FF 000000FF 000000FF 000000FF
+0000FF00 0000FF00 0000FF00 0000FF00 000000FF 000000FF 000000FF 000000FF
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1723_expected.pdf.0.png b/testing/resources/pixel/bug_1723_expected.pdf.0.png
new file mode 100644
index 0000000..46dceb7
--- /dev/null
+++ b/testing/resources/pixel/bug_1723_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1733.in b/testing/resources/pixel/bug_1733.in
new file mode 100644
index 0000000..9812a03
--- /dev/null
+++ b/testing/resources/pixel/bug_1733.in
@@ -0,0 +1,107 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /MediaBox [0 0 100 100]
+ /Count 2
+ /Kids [3 0 R 4 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 5 0 R
+ /Resources <<
+ /XObject 9 0 R
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 5 0 R
+ /Resources <<
+ /XObject 10 0 R
+ >>
+>>
+endobj
+{{object 5 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+100 0 0 100 0 0 cm
+/X1 Do
+Q
+endstream
+endobj
+{{object 6 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /Width 1
+ /Height 1
+ /BitsPerComponent 8
+ /ColorSpace /DeviceRGB
+ /Filter /ASCIIHexDecode
+ {{streamlen}}
+>>
+stream
+FF0000
+endstream
+endobj
+{{object 7 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /Width 1
+ /Height 1
+ /BitsPerComponent 8
+ /ColorSpace /DeviceRGB
+ /Filter /ASCIIHexDecode
+ {{streamlen}}
+>>
+stream
+FFFF00
+endstream
+endobj
+% This object stream contains two different copies of object 9 and object 10.
+% To disambiguate them, a parser must use object 11 0 below, the cross-reference
+% stream, to look up the type 2 entries. Those entries reference this object,
+% and the position of the objects within this object.
+{{object 8 0}} <<
+ /Type /ObjStm
+ /N 4
+ /First 20
+ {{streamlen}}
+>>
+stream
+9 0 9 13 10 26 10 39<</X1 6 0 R>><</X1 7 0 R>><</X1 6 0 R>><</X1 7 0 R>>
+endstream
+endobj
+{{object 11 0}} <<
+ /Type /XRef
+ /Filter /ASCIIHexDecode
+ /Root 1 0 R
+ /Size 11
+ /W [1 2 2]
+ {{streamlen}}
+>>
+stream
+00 0000 FFFF
+01 000F 0000
+01 0044 0000
+01 00A3 0000
+01 0110 0000
+01 017E 0000
+01 01CF 0000
+01 028B 0000
+01 046A 0000
+02 0008 0000
+02 0008 0003
+endstream
+endobj
+{{startxrefobj 11 0}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1733_expected.pdf.0.png b/testing/resources/pixel/bug_1733_expected.pdf.0.png
new file mode 100644
index 0000000..d96c847
--- /dev/null
+++ b/testing/resources/pixel/bug_1733_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1733_expected.pdf.1.png b/testing/resources/pixel/bug_1733_expected.pdf.1.png
new file mode 100644
index 0000000..d1490da
--- /dev/null
+++ b/testing/resources/pixel/bug_1733_expected.pdf.1.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1746.in b/testing/resources/pixel/bug_1746.in
new file mode 100644
index 0000000..ca2a4b2
--- /dev/null
+++ b/testing/resources/pixel/bug_1746.in
@@ -0,0 +1,122 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /MediaBox [0 0 200 200]
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /Resources <<
+ /ExtGState <<
+ /GS0 8 0 R
+ >>
+ /Font <<
+ /F1 5 0 R
+ >>
+ /XObject <<
+ /Img 9 0 R
+ >>
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+/GS0 gs
+q
+0 1 0 rg
+BT
+/F1 2 Tf
+1 0 0 1 50 50 Tm
+(a)Tj
+ET
+Q
+q
+0 0 1 rg
+BT
+/F1 2 Tf
+1 0 0 1 72 80 Tm
+(a)Tj
+ET
+Q
+Q
+endstream
+endobj
+{{object 5 0}} <<
+ /Type /Font
+ /Subtype /Type3
+ /CharProcs <<
+ /a0 7 0 R
+ >>
+ /Encoding 6 0 R
+ /FirstChar 97
+ /FontBBox [0 0 1000 1000]
+ /FontMatrix [1 0 0 1 0 0]
+ /LastChar 97
+ /Widths [60]
+>>
+endobj
+{{object 6 0}} <<
+ /Type /Encoding
+ /BaseEncoding /WinAnsiEncoding
+ /Differences [97 /a0]
+>>
+endobj
+{{object 7 0}} <<
+ {{streamlen}}
+>>
+stream
+50 0 4 -1 48 45 d1
+q
+4 -1 m
+4 45 l
+48 45 l
+48 -1 l
+h
+W n
+q
+44 0 0 46 4.1 -1.1 cm
+/Img Do
+Q
+Q
+endstream
+endobj
+{{object 8 0}} <<
+ /Type /ExtGState
+ /CA 0.5
+ /ca 0.5
+>>
+endobj
+{{object 9 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 1
+ /Decode [1 0]
+ /DecodeParms [null <</Columns 44 /K -1>>]
+ /Filter [/ASCIIHexDecode /CCITTFaxDecode]
+ /Height 46
+ /ImageMask true
+ /Width 44
+ {{streamlen}}
+>>
+stream
+26a08680de081e11e187840830f4137a4df0ef4dbedbffff6ffdaf0c2f1f
+fff21b34c82e33044e7c11a09c205e105e97a5e97a5ffa5fe0a3f5ffafff
+b5c9aaa30bed27adb4ad70da4da5b6128f0c426b216818500100100a
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1746_expected.pdf.0.png b/testing/resources/pixel/bug_1746_expected.pdf.0.png
new file mode 100644
index 0000000..08c41d1
--- /dev/null
+++ b/testing/resources/pixel/bug_1746_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1746_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1746_expected_skia.pdf.0.png
new file mode 100644
index 0000000..0ab21f9
--- /dev/null
+++ b/testing/resources/pixel/bug_1746_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1750.in b/testing/resources/pixel/bug_1750.in
new file mode 100644
index 0000000..ce0f46f
--- /dev/null
+++ b/testing/resources/pixel/bug_1750.in
@@ -0,0 +1,83 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /MediaBox [0 0 200 100]
+ /Resources <<
+ /XObject <<
+ /Img1 6 0 R
+ /Img2 8 0 R
+ >>
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+60 0 0 60 20 20 cm
+/Img1 Do
+Q
+q
+60 0 0 60 120 20 cm
+/Img2 Do
+Q
+endstream
+endobj
+{{object 5 0}} [
+ /Indexed
+ /DeviceGray
+ 1
+ <88 cc>
+]
+endobj
+{{object 6 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 1
+ /ColorSpace 5 0 R
+ /Height 4
+ /Width 4
+ {{streamlen}}
+>>
+stream
+0000
+endstream
+endobj
+{{object 7 0}} [
+ /Indexed
+ /DeviceGray
+ 3
+ <11 44 88 cc>
+]
+endobj
+{{object 8 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 1
+ /ColorSpace 7 0 R
+ /Height 4
+ /Width 4
+ {{streamlen}}
+>>
+stream
+0000
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1750_expected.pdf.0.png b/testing/resources/pixel/bug_1750_expected.pdf.0.png
new file mode 100644
index 0000000..b574e46
--- /dev/null
+++ b/testing/resources/pixel/bug_1750_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1752.in b/testing/resources/pixel/bug_1752.in
new file mode 100644
index 0000000..0ba4a41
--- /dev/null
+++ b/testing/resources/pixel/bug_1752.in
@@ -0,0 +1,75 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /MediaBox [0 0 200 200]
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Resources <<
+ /Font <<
+ /F1 4 0 R
+ >>
+ >>
+ /Contents 6 0 R
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Font
+ /Subtype /TrueType
+ /BaseFont /NotoSansRegular
+ /Encoding /WinAnsiEncoding
+ /FirstChar 65
+ /LastChar 71
+ /FontDescriptor 5 0 R
+ /Name /F1
+ /Widths [250 250 250 250 800 800 800]
+>>
+endobj
+{{object 5 0}} <<
+ /Type /FontDescriptor
+ /CapHeight 750
+ /Descent -250
+ /Flags 32
+ /FontBBox [-503 -250 1240 750]
+ /FontFile2 7 0 R
+ /FontName /NotoSansRegular
+ /FontWeight 400
+ /ItalicAngle 0
+ /StemV 52
+>>
+endobj
+{{object 6 0}} <<
+ {{streamlen}}
+>>
+stream
+BT
+20 100 Td
+/F1 70 Tf
+(ABCD) Tj
+0 -80 Td
+(EFG) Tj
+ET
+endstream
+endobj
+{{object 7 0}} <<
+ /Filter [/ASCIIHexDecode /FlateDecode]
+ {{streamlen}}
+ /Length1 11200
+>>
+stream
+{{include ../bug_1752_truetype_font.fragment}}
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1752_expected.pdf.0.png b/testing/resources/pixel/bug_1752_expected.pdf.0.png
new file mode 100644
index 0000000..ed9be10
--- /dev/null
+++ b/testing/resources/pixel/bug_1752_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1752_expected_mac.pdf.0.png b/testing/resources/pixel/bug_1752_expected_mac.pdf.0.png
new file mode 100644
index 0000000..a8cb3ba
--- /dev/null
+++ b/testing/resources/pixel/bug_1752_expected_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1752_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1752_expected_skia.pdf.0.png
new file mode 100644
index 0000000..9cb41b8
--- /dev/null
+++ b/testing/resources/pixel/bug_1752_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1772.in b/testing/resources/pixel/bug_1772.in
new file mode 100644
index 0000000..026b713
--- /dev/null
+++ b/testing/resources/pixel/bug_1772.in
@@ -0,0 +1,93 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents [4 0 R]
+ /MediaBox [0 0 100 100]
+ /Resources <<
+ /ProcSet [/PDF /Text]
+ /ColorSpace <<
+ /CS1 [/Pattern]
+ >>
+ /Font <<
+ /F1 5 0 R
+ >>
+ /Pattern <<
+ /P1 6 0 R
+ >>
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+BT
+/CS1 CS
+/P1 SCN
+/F1 1 Tf
+1 Tr
+20 0 0 20 20 30 Tm
+(S) Tj
+ET
+BT
+/CS1 CS
+/P1 SCN
+/F1 4 Tf
+1 Tr
+5 0 0 5 50 80 Tm
+(S) Tj
+ET
+BT
+/CS1 CS
+/P1 SCN
+/F1 20 Tf
+1 Tr
+1 0 0 1 70 40 Tm
+(S) Tj
+ET
+endstream
+endobj
+{{object 5 0}} <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Helvetica
+>>
+endobj
+{{object 6 0}} <<
+ /Type /Pattern
+ /PatternType 2
+ /Shading 7 0 R
+>>
+endobj
+{{object 7 0}} <<
+ /ShadingType 3
+ /ColorSpace /DeviceRGB
+ /Coords [0.0 0.0 0.0 0.0 0.0 0.005] % Concentric circles
+ /Function 8 0 R
+ /Extend [true true]
+ >>
+endobj
+{{object 8 0}} <<
+ /FunctionType 2
+ /Domain [0.0 1.0]
+ /C0 [0 0 0]
+ /C1 [0 1 0]
+ /N 1
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1772_expected.pdf.0.png b/testing/resources/pixel/bug_1772_expected.pdf.0.png
new file mode 100644
index 0000000..7056d87
--- /dev/null
+++ b/testing/resources/pixel/bug_1772_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1772_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1772_expected_skia.pdf.0.png
new file mode 100644
index 0000000..477b2a2
--- /dev/null
+++ b/testing/resources/pixel/bug_1772_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1774.in b/testing/resources/pixel/bug_1774.in
new file mode 100644
index 0000000..d80b429
--- /dev/null
+++ b/testing/resources/pixel/bug_1774.in
@@ -0,0 +1,47 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 200 200]
+ /Contents 4 0 R
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+0.001 w
+30 150 m
+170 150 l
+S
+Q
+q
+0.01 w
+30 95 m
+170 95 l
+S
+Q
+q
+0.1 w
+30 40 m
+170 40 l
+S
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1774_expected.pdf.0.png b/testing/resources/pixel/bug_1774_expected.pdf.0.png
new file mode 100644
index 0000000..32bcba3
--- /dev/null
+++ b/testing/resources/pixel/bug_1774_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1774_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1774_expected_skia.pdf.0.png
new file mode 100644
index 0000000..40ec74e
--- /dev/null
+++ b/testing/resources/pixel/bug_1774_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1822.in b/testing/resources/pixel/bug_1822.in
new file mode 100644
index 0000000..4075bb7
--- /dev/null
+++ b/testing/resources/pixel/bug_1822.in
@@ -0,0 +1,34 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /MediaBox [0 0 400 400]
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+[1.8 1.8] 96636760 d
+1 i
+100 300 m
+200 300 l
+S
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1822_expected.pdf.0.png b/testing/resources/pixel/bug_1822_expected.pdf.0.png
new file mode 100644
index 0000000..913f33f
--- /dev/null
+++ b/testing/resources/pixel/bug_1822_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1822_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1822_expected_skia.pdf.0.png
new file mode 100644
index 0000000..2467b79
--- /dev/null
+++ b/testing/resources/pixel/bug_1822_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1845.in b/testing/resources/pixel/bug_1845.in
new file mode 100644
index 0000000..9fd8052
--- /dev/null
+++ b/testing/resources/pixel/bug_1845.in
@@ -0,0 +1,80 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /MediaBox [0 0 96 96]
+ /Resources <<
+ /XObject <<
+ /X0 5 0 R
+ >>
+ >>
+>>
+endobj
+
+% Cross formed by red horizontal rectangle overlaid with blue vertical
+% rectangle.
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+ % Red horizontal rectangle defined by clip.
+ 8 32 80 32 re W* n
+ 1 0 0 rg
+ 0 0 96 96 re f
+Q
+q
+ % Blue vertical rectangle defined by masked image.
+ 32 0 0 80 32 8 cm
+ /X0 Do
+Q
+endstream
+endobj
+
+% Single pixel image with /SMask to trigger masked image rendering.
+{{object 5 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /Width 1
+ /Height 1
+ /ColorSpace /DeviceRGB
+ /BitsPerComponent 8
+ /SMask 6 0 R
+ /Filter /ASCIIHexDecode
+ {{streamlen}}
+>>
+stream
+00 00 FF
+endstream
+endobj
+{{object 6 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /Width 1
+ /Height 1
+ /ColorSpace /DeviceGray
+ /BitsPerComponent 8
+ /Filter /ASCIIHexDecode
+ {{streamlen}}
+>>
+stream
+80
+endstream
+endobj
+
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1845_expected.pdf.0.png b/testing/resources/pixel/bug_1845_expected.pdf.0.png
new file mode 100644
index 0000000..b246272
--- /dev/null
+++ b/testing/resources/pixel/bug_1845_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1847.in b/testing/resources/pixel/bug_1847.in
new file mode 100644
index 0000000..42d4ef9
--- /dev/null
+++ b/testing/resources/pixel/bug_1847.in
@@ -0,0 +1,65 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /MediaBox [0 0 400 400]
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Annots [4 0 R]
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Annot
+ /Subtype /Highlight
+ /AP <<
+ /N 5 0 R
+ >>
+ /F 4
+ /P 3 0 R
+ /Rect 6 0 R
+>>
+endobj
+{{object 5 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /BBox 6 0 R
+ /ProcSet [/PDF]
+ /Resources <<
+ /ExtGState <<
+ /R0 <<
+ /Type /ExtGState
+ /AIS false
+ /BM /Multiply
+ >>
+ >>
+ >>
+ {{streamlen}}
+>>
+stream
+/R0 gs
+1 1 0 rg
+1 w
+72 305 m
+68 309 68 317 72 321 c
+132 321 l
+136 317 136 309 132 305 c
+f
+endstream
+endobj
+{{object 6 0}}
+[67 304 137 322]
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1847_expected.pdf.0.png b/testing/resources/pixel/bug_1847_expected.pdf.0.png
new file mode 100644
index 0000000..1e9d7fb
--- /dev/null
+++ b/testing/resources/pixel/bug_1847_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1847_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1847_expected_skia.pdf.0.png
new file mode 100644
index 0000000..f201f8e
--- /dev/null
+++ b/testing/resources/pixel/bug_1847_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1883.in b/testing/resources/pixel/bug_1883.in
new file mode 100644
index 0000000..79593bb
--- /dev/null
+++ b/testing/resources/pixel/bug_1883.in
@@ -0,0 +1,92 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 4
+ /Kids [3 0 R 4 0 R 5 0 R 6 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Annots [7 0 R 8 0 R]
+ /MediaBox [0 0 612 792]
+ /Parent 2 0 R
+ /Rotate 0
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Page
+ /Annots [7 0 R 8 0 R]
+ /MediaBox [0 0 792 612]
+ /Parent 2 0 R
+ /Rotate 90
+>>
+endobj
+{{object 5 0}} <<
+ /Type /Page
+ /Annots [7 0 R 8 0 R]
+ /MediaBox [0 0 612 792]
+ /Parent 2 0 R
+ /Rotate 180
+>>
+endobj
+{{object 6 0}} <<
+ /Type /Page
+ /Annots [7 0 R 8 0 R]
+ /MediaBox [0 0 792 612]
+ /Parent 2 0 R
+ /Rotate 270
+>>
+endobj
+{{object 7 0}} <<
+ /Type /Annot
+ /Subtype /Line
+ /AP <<
+ /N 9 0 R
+ >>
+ % NoRotate
+ /F 16
+ /L [100 50 100 150]
+ /P 3 0 R
+ /Rect [90 370 270 500]
+>>
+endobj
+{{object 8 0}} <<
+ /Type /Annot
+ /Subtype /Line
+ /AP <<
+ /N 9 0 R
+ >>
+ /F 0
+ /L [100 50 100 150]
+ /P 3 0 R
+ /Rect [190 470 370 600]
+>>
+endobj
+{{object 9 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /BBox [0 0 360 130]
+ {{streamlen}}
+>>
+stream
+10 w
+1 0 0 RG
+30 65 m
+150 65 l
+S
+120 45 m
+150 65 l
+120 85 l
+S
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1883_expected.pdf.0.png b/testing/resources/pixel/bug_1883_expected.pdf.0.png
new file mode 100644
index 0000000..4bc6992
--- /dev/null
+++ b/testing/resources/pixel/bug_1883_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1883_expected.pdf.1.png b/testing/resources/pixel/bug_1883_expected.pdf.1.png
new file mode 100644
index 0000000..f18c1d7
--- /dev/null
+++ b/testing/resources/pixel/bug_1883_expected.pdf.1.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1883_expected.pdf.2.png b/testing/resources/pixel/bug_1883_expected.pdf.2.png
new file mode 100644
index 0000000..a3093ff
--- /dev/null
+++ b/testing/resources/pixel/bug_1883_expected.pdf.2.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1883_expected.pdf.3.png b/testing/resources/pixel/bug_1883_expected.pdf.3.png
new file mode 100644
index 0000000..e6e3988
--- /dev/null
+++ b/testing/resources/pixel/bug_1883_expected.pdf.3.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1883_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1883_expected_skia.pdf.0.png
new file mode 100644
index 0000000..8560bfe
--- /dev/null
+++ b/testing/resources/pixel/bug_1883_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1883_expected_skia.pdf.1.png b/testing/resources/pixel/bug_1883_expected_skia.pdf.1.png
new file mode 100644
index 0000000..0660877
--- /dev/null
+++ b/testing/resources/pixel/bug_1883_expected_skia.pdf.1.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1883_expected_skia.pdf.2.png b/testing/resources/pixel/bug_1883_expected_skia.pdf.2.png
new file mode 100644
index 0000000..f0050f8
--- /dev/null
+++ b/testing/resources/pixel/bug_1883_expected_skia.pdf.2.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1883_expected_skia.pdf.3.png b/testing/resources/pixel/bug_1883_expected_skia.pdf.3.png
new file mode 100644
index 0000000..88d3e25
--- /dev/null
+++ b/testing/resources/pixel/bug_1883_expected_skia.pdf.3.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1922.in b/testing/resources/pixel/bug_1922.in
new file mode 100644
index 0000000..c7b005b
--- /dev/null
+++ b/testing/resources/pixel/bug_1922.in
@@ -0,0 +1,66 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /MediaBox [0 0 100 100]
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 5 0 R
+ /Resources <<
+ /Font <<
+ /F1 4 0 R
+ >>
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Font
+ /Subtype /TrueType
+ /BaseFont /TimesNewRoman,Bold
+ /Encoding /WinAnsiEncoding
+ /FirstChar 32
+ /LastChar 121
+ /Widths [
+ 230.769 307.692 538.462 538.462 538.462 1153.846 846.154 307.692
+ 307.692 307.692 538.462 538.462 230.769 307.692 230.769 307.692
+ 538.462 538.462 538.462 538.462 538.462 538.462 538.462 538.462
+ 538.462 538.462 307.692 307.692 538.462 538.462 538.462 538.462
+ 923.077 692.308 692.308 692.308 692.308 538.462 538.462 692.308
+ 692.308 307.692 538.462 692.308 615.385 846.154 692.308 769.231
+ 615.385 769.231 692.308 615.385 615.385 615.385 692.308 1000.000
+ 692.308 615.385 615.385 307.692 307.692 307.692 615.385 538.462
+ 307.692 461.538 461.538 461.538 461.538 461.538 307.692 538.462
+ 538.462 307.692 307.692 615.385 307.692 769.231 538.462 461.538
+ 461.538 461.538 461.538 461.538 307.692 538.462 384.615 615.385
+ 461.538 461.538
+ ]
+>>
+endobj
+{{object 5 0}} <<
+ {{streamlen}}
+>>
+stream
+2 J
+BT
+0 0 0 rg
+/F1 20 Tf
+10 40 Td
+[(U) -107.72357178 (n) -14.68963623 (i) 31.01132202
+(v) -125.67745972 (e) 14.6895752 (r) 35.90786743 (s) 57.12615967
+(i) 31.01132202 (t) -11.42520142 (y) -48.96524048]TJ
+ET
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
\ No newline at end of file
diff --git a/testing/resources/pixel/bug_1922_expected.pdf.0.png b/testing/resources/pixel/bug_1922_expected.pdf.0.png
new file mode 100644
index 0000000..73057ac
--- /dev/null
+++ b/testing/resources/pixel/bug_1922_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1922_expected_agg_mac.pdf.0.png b/testing/resources/pixel/bug_1922_expected_agg_mac.pdf.0.png
new file mode 100644
index 0000000..2caa41a
--- /dev/null
+++ b/testing/resources/pixel/bug_1922_expected_agg_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1949.in b/testing/resources/pixel/bug_1949.in
new file mode 100644
index 0000000..5e7afd3
--- /dev/null
+++ b/testing/resources/pixel/bug_1949.in
@@ -0,0 +1,133 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /MediaBox [0 0 44 24]
+ /Resources <<
+ /ExtGState <<
+ /GSHalfAlphaConstant 5 0 R
+ /GSHalfAlphaMask 6 0 R
+ >>
+ /XObject <<
+ /HalfAlphaSquare 8 0 R
+ >>
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+
+% Opaque black background.
+q
+ 0 0 0 rg
+ 0 0 44 24 re
+ f
+Q
+
+% Transparency group drawn with constant alpha.
+q
+ 1 0 0 1 4 4 cm
+ /GSHalfAlphaConstant gs
+
+ /HalfAlphaSquare Do
+Q
+
+% Transparency group drawn with a soft mask.
+q
+ 1 0 0 1 24 4 cm
+ /GSHalfAlphaMask gs
+
+ /HalfAlphaSquare Do
+Q
+
+endstream
+endobj
+
+% Graphics state with a 0.5 non-stroking alpha constant.
+{{object 5 0}} <<
+ /Type /ExtGState
+ /ca 0.5
+>>
+endobj
+
+% Graphics state with a soft mask containing a 0.5 alpha central hole.
+{{object 6 0}} <<
+ /Type /ExtGState
+ /SMask <<
+ /Type /Mask
+ /S /Luminosity
+ /G 7 0 R
+ >>
+>>
+endobj
+{{object 7 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /BBox [0 0 16 16]
+ /Group <<
+ /Type /Group
+ /S /Transparency
+ /CS /DeviceGray
+ /I true
+ >>
+ {{streamlen}}
+>>
+stream
+q
+ 1 g
+ 0 0 16 16 re
+ f
+
+ 0.5 g
+ 4 4 8 8 re
+ f
+Q
+endstream
+endobj
+
+% Transparency group containing a 0.5 alpha white square.
+{{object 8 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /BBox [0 0 16 16]
+ /Group <<
+ /Type /Group
+ /S /Transparency
+ /I true
+ >>
+ /Resources <<
+ /ExtGState <<
+ /GSHalfAlphaConstant 5 0 R
+ >>
+ >>
+ {{streamlen}}
+>>
+stream
+q
+ /GSHalfAlphaConstant gs
+
+ 1 1 1 rg
+ 0 0 16 16 re
+ f
+Q
+endstream
+endobj
+
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1949_expected.pdf.0.png b/testing/resources/pixel/bug_1949_expected.pdf.0.png
new file mode 100644
index 0000000..c215044
--- /dev/null
+++ b/testing/resources/pixel/bug_1949_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1949_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1949_expected_skia.pdf.0.png
new file mode 100644
index 0000000..6d4ea6d
--- /dev/null
+++ b/testing/resources/pixel/bug_1949_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1963.in b/testing/resources/pixel/bug_1963.in
new file mode 100644
index 0000000..d79560b
--- /dev/null
+++ b/testing/resources/pixel/bug_1963.in
@@ -0,0 +1,73 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /MediaBox [300 500 350 530]
+ /Resources <<
+ /XObject <<
+ /X1 5 0 R
+ /X2 6 0 R
+ >>
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+ 1 0 0 1 301.65 515.4 cm
+ /X1 Do
+Q
+q
+ 1 0 0 1 304.05 503.7 cm
+ /X2 Do
+Q
+endstream
+endobj
+{{object 5 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /BBox [301.65 515.4 342.75 527.4]
+ /Matrix [1 0 0 1 -301.65 -515.4]
+ {{streamlen}}
+>>
+stream
+1 0 0 RG
+2 w
+305.400 521.400 m
+339.000 521.400 l
+S
+endstream
+endobj
+{{object 6 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /BBox [304.05 503.7 345.15 515.7]
+ /Matrix [1 0 0 1 -304.05 -503.7]
+ {{streamlen}}
+>>
+stream
+1 0 0 RG
+2 w
+307.800 510.000 m
+341.400 509.400 l
+S
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1963_expected.pdf.0.png b/testing/resources/pixel/bug_1963_expected.pdf.0.png
new file mode 100644
index 0000000..a2aec8c
--- /dev/null
+++ b/testing/resources/pixel/bug_1963_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1963_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1963_expected_skia.pdf.0.png
new file mode 100644
index 0000000..2d9ddae
--- /dev/null
+++ b/testing/resources/pixel/bug_1963_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1966.in b/testing/resources/pixel/bug_1966.in
new file mode 100644
index 0000000..b52a6cb
--- /dev/null
+++ b/testing/resources/pixel/bug_1966.in
@@ -0,0 +1,121 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+ /MediaBox [0 0 128.571 128.571]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 7 0 R
+ /Resources <<
+ /XObject <<
+ /Im1 4 0 R
+ >>
+ /Pattern <<
+ /P1 5 0 R
+ /P2 6 0 R
+ >>
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 1
+ /Filter /ASCIIHexDecode
+ /Height 40
+ /ImageMask true
+ /Width 80
+ {{streamlen}}
+>>
+stream
+FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000FFFF
+FFFF000000000000FFFFFFFF0FFFFFFFFFF0FFFFFFFF0FFFFFFFFFF0FFFF
+FFFF0FFFFFFFFFF0FFFFFFFF0FFFFFFFFFF0FFFFFFFF000000000000FFFF
+FFFF000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+FFFF000000000000FFFFFFFF000000000000FFFFFFFF0FFFFFFFFFF0FFFF
+FFFF0FFFFFFFFFF0FFFFFFFF0FFFFFFFFFF0FFFFFFFF0FFFFFFFFFF0FFFF
+FFFF000000000000FFFFFFFF000000000000FFFFFFFFFFFFFFFFFFFFFFFF
+FFFFFFFFFFFFFFFFFFFF
+endstream
+endobj
+{{object 5 0}} <<
+ /Type /Pattern
+ /PaintType 1
+ /PatternType 1
+ /TilingType 1
+ /BBox [0 0 128.571 128.571]
+ /XStep 128.571
+ /YStep 128.571
+ {{streamlen}}
+>>
+stream
+q
+0.0 0.0 0.0 rg
+0 128.571 m
+128.571 128.571 l
+128.571 64 l
+0 64 l
+f
+Q
+endstream
+endobj
+{{object 6 0}} <<
+ /Type /Pattern
+ /PaintType 1
+ /PatternType 1
+ /TilingType 1
+ /BBox [0 0 128.571 128.571]
+ /XStep 128.571
+ /YStep 128.571
+ {{streamlen}}
+>>
+stream
+% second pattern
+q
+0.0 0.0 0.0 rg
+0 64 m
+128.571 64 l
+128.571 0 l
+0 0 l
+f
+Q
+endstream
+endobj
+{{object 7 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+/Pattern cs
+/P1 scn
+q
+128.571 0 0 128.571 0 0 cm
+/Im1 Do
+Q
+/P2 scn
+q
+128.571 0 0 128.571 0 0 cm
+/Im1 Do
+Q
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
\ No newline at end of file
diff --git a/testing/resources/pixel/bug_1966_expected.pdf.0.png b/testing/resources/pixel/bug_1966_expected.pdf.0.png
new file mode 100644
index 0000000..47d12bd
--- /dev/null
+++ b/testing/resources/pixel/bug_1966_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1966_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1966_expected_skia.pdf.0.png
new file mode 100644
index 0000000..134ca64
--- /dev/null
+++ b/testing/resources/pixel/bug_1966_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1972_1.in b/testing/resources/pixel/bug_1972_1.in
new file mode 100644
index 0000000..e537e94
--- /dev/null
+++ b/testing/resources/pixel/bug_1972_1.in
@@ -0,0 +1,36 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 200 100]
+ /Contents 4 0 R
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+5 0 0 5 0 0 cm
+0 1 0 RG
+0 0 m
+16000 1420 l
+S
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1972_1_expected.pdf.0.png b/testing/resources/pixel/bug_1972_1_expected.pdf.0.png
new file mode 100644
index 0000000..bca30df
--- /dev/null
+++ b/testing/resources/pixel/bug_1972_1_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1972_2.in b/testing/resources/pixel/bug_1972_2.in
new file mode 100644
index 0000000..4c1ad95
--- /dev/null
+++ b/testing/resources/pixel/bug_1972_2.in
@@ -0,0 +1,36 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 200 100]
+ /Contents 4 0 R
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+5 0 0 5 0 0 cm
+0 1 0 RG
+-9000 -9000 m
+500 500 l
+S
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1972_2_expected.pdf.0.png b/testing/resources/pixel/bug_1972_2_expected.pdf.0.png
new file mode 100644
index 0000000..19b8c8a
--- /dev/null
+++ b/testing/resources/pixel/bug_1972_2_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1972_3.in b/testing/resources/pixel/bug_1972_3.in
new file mode 100644
index 0000000..5d84df7
--- /dev/null
+++ b/testing/resources/pixel/bug_1972_3.in
@@ -0,0 +1,36 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 200 100]
+ /Contents 4 0 R
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+5 0 0 5 0 0 cm
+0 1 0 RG
+0 0 m
+2 10000 20 9000 40 -10000 c
+S
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1972_3_expected.pdf.0.png b/testing/resources/pixel/bug_1972_3_expected.pdf.0.png
new file mode 100644
index 0000000..d0f97c9
--- /dev/null
+++ b/testing/resources/pixel/bug_1972_3_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1973.in b/testing/resources/pixel/bug_1973.in
new file mode 100644
index 0000000..1d5602d
--- /dev/null
+++ b/testing/resources/pixel/bug_1973.in
@@ -0,0 +1,81 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [ 3 0 R ]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /MediaBox [ 240 570 345 650 ]
+ /Resources <<
+ /XObject <<
+ /Img 5 0 R
+ >>
+ >>
+>>
+endobj
+
+% Content stream matching coordinates in form_button0.pdf.
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+ 89.555968 0 0 67.166976 247.7222 576.8333 cm
+ q
+ 1 0 0 rg
+ 0 0 1 1 re
+ f
+ Q
+ /Img Do
+Q
+endstream
+endobj
+
+% A 1024x768 image with a single (0x00), indexed color (0x00FF00).
+{{object 5 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 8
+ /ColorSpace [ /Indexed /DeviceRGB 0 <00FF00> ]
+ /Filter [ /ASCIIHexDecode /FlateDecode ]
+ /Height 768
+ /Width 1024
+ {{streamlen}}
+>>
+stream
+78DAEDC13101000000C2A0F54F6D067FA00000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000000000000000000000000000000000
+000000000000000000000000000000000000003E0300B40001
+endstream
+endobj
+
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1973_expected.pdf.0.png b/testing/resources/pixel/bug_1973_expected.pdf.0.png
new file mode 100644
index 0000000..8d75b62
--- /dev/null
+++ b/testing/resources/pixel/bug_1973_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1976.in b/testing/resources/pixel/bug_1976.in
new file mode 100644
index 0000000..696f5da
--- /dev/null
+++ b/testing/resources/pixel/bug_1976.in
@@ -0,0 +1,83 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /MediaBox [0 0 96 96]
+ /Resources <<
+ /XObject <<
+ /X0 5 0 R
+ >>
+ >>
+>>
+endobj
+
+% Green and red checkerboard. When scaled down, the green and red should blend
+% together into a darker yellow.
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+ % Solid green square.
+ 0 1 0 rg
+ 16 16 64 64 re f
+Q
+q
+ % Transparent and red checkerboard.
+ 64 0 0 64 16 16 cm
+ /X0 Do
+Q
+endstream
+endobj
+
+% Single pixel image with /SMask to trigger masked image rendering.
+{{object 5 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 8
+ /ColorSpace /DeviceRGB
+ /Filter /ASCIIHexDecode
+ /Height 1
+ /SMask 6 0 R
+ /Width 1
+ {{streamlen}}
+>>
+stream
+FF 00 00
+endstream
+endobj
+
+% Grayscale checkerboard to trigger 8-bit to 8-bit scaling. Scaling down by a
+% multiple of 2 using nearest neighbor gives particularly bad results.
+{{object 6 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 8
+ /ColorSpace /DeviceGray
+ /Filter [/ASCII85Decode /FlateDecode]
+ /Height 128
+ /Width 128
+ {{streamlen}}
+>>
+stream
+GhVQ20b"*_#f&.lRiX?CBI7,$dqQl"iofLfkND$kkND$kkND$kkND$kkND$kkND$kkND$kkND$kkND$k
+kND$kkND$kkND$kkND$kkND$kkN@?Q?he3kdJ~>
+endstream
+endobj
+
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1976_expected.pdf.0.png b/testing/resources/pixel/bug_1976_expected.pdf.0.png
new file mode 100644
index 0000000..59bfc84
--- /dev/null
+++ b/testing/resources/pixel/bug_1976_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1983.in b/testing/resources/pixel/bug_1983.in
new file mode 100644
index 0000000..0167258
--- /dev/null
+++ b/testing/resources/pixel/bug_1983.in
@@ -0,0 +1,43 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents [4 0 R]
+ /MediaBox [0 0 100 100]
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+20 w
+q
+1 0 0 1 20 10 cm
+1 J
+% The first dot
+10 40 m
+h
+S
+% The second dot
+50 40 m
+50 40 l
+h
+S
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1983_expected.pdf.0.png b/testing/resources/pixel/bug_1983_expected.pdf.0.png
new file mode 100644
index 0000000..ea59974
--- /dev/null
+++ b/testing/resources/pixel/bug_1983_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1983_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1983_expected_skia.pdf.0.png
new file mode 100644
index 0000000..1e341ac
--- /dev/null
+++ b/testing/resources/pixel/bug_1983_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1986.in b/testing/resources/pixel/bug_1986.in
new file mode 100644
index 0000000..9e35fb3
--- /dev/null
+++ b/testing/resources/pixel/bug_1986.in
@@ -0,0 +1,61 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /MediaBox [0 0 612 792]
+ /Resources <<
+ /XObject <<
+ /Im0 5 0 R
+ >>
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+612 0 0 792 0 0 cm
+/Im0 Do
+Q
+endstream
+endobj
+{{object 5 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /Filter [6 0 R /LZWDecode 7 0 R]
+ /Width 612
+ /Height 792
+ {{streamlen}}
+>>
+stream
+80002040c351404020068290e0a8100028663a1e4e06a380c8410d004522d0d16c68d10d0b1a4d06
+439408061881008c8000181c0f180003cc66f361c80330084358d1a31bfc9eff288005f189549a51
+30a24ae5947a44b00e0100d3ea3507f948000c86d400a040200002ff2e0002640201209050b35a2c
+f69b4bfe5625af10ce465309d0ca6410188f220279c0ca6e251408a47101d8ca72399a4de6e100c8
+5c33170c1fe9000432303faf3fd26cf5a14022502a2bdfe7f369a0b480980c12097d1801be801c05
+0005c57bf818daa6ee5fed9801
+endstream
+endobj
+{{object 6 0}}
+/ASCIIHexDecode
+enbobj
+{{object 7 0}}
+/JPXDecode
+enbobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1986_expected.pdf.0.png b/testing/resources/pixel/bug_1986_expected.pdf.0.png
new file mode 100644
index 0000000..e1b3d43
--- /dev/null
+++ b/testing/resources/pixel/bug_1986_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1995.in b/testing/resources/pixel/bug_1995.in
new file mode 100644
index 0000000..448fa58
--- /dev/null
+++ b/testing/resources/pixel/bug_1995.in
@@ -0,0 +1,81 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /Group <<
+ /Type /Group
+ /S /Transparency
+ >>
+ /MediaBox [0 0 40 40]
+ /Resources <<
+ /ExtGState <<
+ /GSMask <<
+ /SMask <<
+ /Type /Mask
+ /S /Alpha
+ /G 5 0 R
+ >>
+ >>
+ >>
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+ 1 0 0 1 5 5 cm
+
+ % Red square.
+ 1 0 0 rg
+ 0 0 30 30 re
+ f
+
+ % Blue square with soft mask dictionary.
+ /GSMask gs
+ 0 0 1 rg
+ 0 0 30 30 re
+ f
+Q
+endstream
+endobj
+
+% Soft mask dictionary with a central hole.
+{{object 5 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /BBox [0 0 30 30]
+ /Group <<
+ /Type /Group
+ /S /Transparency
+ /I true
+ >>
+ {{streamlen}}
+>>
+stream
+q
+ 0 0 0 RG
+ 15 w
+ 0 0 30 30 re
+ S
+Q
+endstream
+endobj
+
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_1995_expected.pdf.0.png b/testing/resources/pixel/bug_1995_expected.pdf.0.png
new file mode 100644
index 0000000..daff3e9
--- /dev/null
+++ b/testing/resources/pixel/bug_1995_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_1995_expected_skia.pdf.0.png b/testing/resources/pixel/bug_1995_expected_skia.pdf.0.png
new file mode 100644
index 0000000..97ac01f
--- /dev/null
+++ b/testing/resources/pixel/bug_1995_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_2001.pdf b/testing/resources/pixel/bug_2001.pdf
new file mode 100644
index 0000000..f73831d
--- /dev/null
+++ b/testing/resources/pixel/bug_2001.pdf
Binary files differ
diff --git a/testing/resources/pixel/bug_2001_expected.pdf.0.png b/testing/resources/pixel/bug_2001_expected.pdf.0.png
new file mode 100644
index 0000000..fd353a9
--- /dev/null
+++ b/testing/resources/pixel/bug_2001_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_237527_1.in b/testing/resources/pixel/bug_237527_1.in
new file mode 100644
index 0000000..503fa2a
--- /dev/null
+++ b/testing/resources/pixel/bug_237527_1.in
@@ -0,0 +1,37 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Annots [4 0 R]
+ /MediaBox [0 0 300 300]
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Annot
+ /Subtype /Link
+ /A <<
+ /Type /Action
+ /S /URI
+ /URI (www.google.com)
+ >>
+ /Border [0 0 1]
+ /C [0 1 1]
+ /H /I
+ /Rect [100 100 200 120]
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_237527_1_expected.pdf.0.png b/testing/resources/pixel/bug_237527_1_expected.pdf.0.png
new file mode 100644
index 0000000..d8c8148
--- /dev/null
+++ b/testing/resources/pixel/bug_237527_1_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_237527_1_expected_skia.pdf.0.png b/testing/resources/pixel/bug_237527_1_expected_skia.pdf.0.png
new file mode 100644
index 0000000..eef577e
--- /dev/null
+++ b/testing/resources/pixel/bug_237527_1_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_237527_2.in b/testing/resources/pixel/bug_237527_2.in
new file mode 100644
index 0000000..fd4eefa
--- /dev/null
+++ b/testing/resources/pixel/bug_237527_2.in
@@ -0,0 +1,42 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Annots [4 0 R]
+ /MediaBox [0 0 300 300]
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Annot
+ /Subtype /Link
+ /A <<
+ /Type /Action
+ /S /URI
+ /URI (http://www.google.com)
+ >>
+ /BS <<
+ /Type /Border
+ /S /U
+ /W 1
+ >>
+ /Border [0 0 1]
+ /C [0 1 1]
+ /H /I
+ /Rect [100 100 200 120]
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_237527_2_expected.pdf.0.png b/testing/resources/pixel/bug_237527_2_expected.pdf.0.png
new file mode 100644
index 0000000..14b04de
--- /dev/null
+++ b/testing/resources/pixel/bug_237527_2_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_304_expected.pdf.0.png b/testing/resources/pixel/bug_304_expected.pdf.0.png
index e1b3d43..62de87b 100644
--- a/testing/resources/pixel/bug_304_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_304_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_451366_expected_skia.pdf.0.png b/testing/resources/pixel/bug_451366_expected_skia.pdf.0.png
new file mode 100644
index 0000000..94b4524
--- /dev/null
+++ b/testing/resources/pixel/bug_451366_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_491_invisible_expected_skia.pdf.0.png b/testing/resources/pixel/bug_491_invisible_expected_skia.pdf.0.png
new file mode 100644
index 0000000..518a266
--- /dev/null
+++ b/testing/resources/pixel/bug_491_invisible_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_491_unspecified_expected_skia.pdf.0.png b/testing/resources/pixel/bug_491_unspecified_expected_skia.pdf.0.png
new file mode 100644
index 0000000..10665ac
--- /dev/null
+++ b/testing/resources/pixel/bug_491_unspecified_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_491_visible_expected_skia.pdf.0.png b/testing/resources/pixel/bug_491_visible_expected_skia.pdf.0.png
new file mode 100644
index 0000000..10665ac
--- /dev/null
+++ b/testing/resources/pixel/bug_491_visible_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_492.in b/testing/resources/pixel/bug_492.in
index 45c4a6f..e2922db 100644
--- a/testing/resources/pixel/bug_492.in
+++ b/testing/resources/pixel/bug_492.in
@@ -35,7 +35,7 @@
/JS 21 0 R
>>
endobj
-% JS program to exexute
+% JS program to execute
{{object 21 0}} <<
>>
stream
diff --git a/testing/resources/pixel/bug_524043_1_expected.pdf.0.png b/testing/resources/pixel/bug_524043_1_expected.pdf.0.png
index eabf957..a7e1fd7 100644
--- a/testing/resources/pixel/bug_524043_1_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_524043_1_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_524043_1_expected_agg_mac.pdf.0.png b/testing/resources/pixel/bug_524043_1_expected_agg_mac.pdf.0.png
new file mode 100644
index 0000000..438e9f3
--- /dev/null
+++ b/testing/resources/pixel/bug_524043_1_expected_agg_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_524043_1_expected_mac.pdf.0.png b/testing/resources/pixel/bug_524043_1_expected_mac.pdf.0.png
deleted file mode 100644
index b85b9d0..0000000
--- a/testing/resources/pixel/bug_524043_1_expected_mac.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_524043_1_expected_win.pdf.0.png b/testing/resources/pixel/bug_524043_1_expected_win.pdf.0.png
deleted file mode 100644
index b2c134a..0000000
--- a/testing/resources/pixel/bug_524043_1_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_524043_2_expected.pdf.0.png b/testing/resources/pixel/bug_524043_2_expected.pdf.0.png
index 04304fe..4dd2672 100644
--- a/testing/resources/pixel/bug_524043_2_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_524043_2_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_524043_2_expected_agg_mac.pdf.0.png b/testing/resources/pixel/bug_524043_2_expected_agg_mac.pdf.0.png
new file mode 100644
index 0000000..e3bf21f
--- /dev/null
+++ b/testing/resources/pixel/bug_524043_2_expected_agg_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_524043_2_expected_mac.pdf.0.png b/testing/resources/pixel/bug_524043_2_expected_mac.pdf.0.png
deleted file mode 100644
index ecede7d..0000000
--- a/testing/resources/pixel/bug_524043_2_expected_mac.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_524043_2_expected_win.pdf.0.png b/testing/resources/pixel/bug_524043_2_expected_win.pdf.0.png
deleted file mode 100644
index 786d963..0000000
--- a/testing/resources/pixel/bug_524043_2_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_524043_3_expected.pdf.0.png b/testing/resources/pixel/bug_524043_3_expected.pdf.0.png
index eabf957..a7e1fd7 100644
--- a/testing/resources/pixel/bug_524043_3_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_524043_3_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_524043_3_expected_agg_mac.pdf.0.png b/testing/resources/pixel/bug_524043_3_expected_agg_mac.pdf.0.png
new file mode 100644
index 0000000..e794355
--- /dev/null
+++ b/testing/resources/pixel/bug_524043_3_expected_agg_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_524043_3_expected_mac.pdf.0.png b/testing/resources/pixel/bug_524043_3_expected_mac.pdf.0.png
deleted file mode 100644
index b85b9d0..0000000
--- a/testing/resources/pixel/bug_524043_3_expected_mac.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_524043_3_expected_win.pdf.0.png b/testing/resources/pixel/bug_524043_3_expected_win.pdf.0.png
deleted file mode 100644
index b2c134a..0000000
--- a/testing/resources/pixel/bug_524043_3_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_524043_4_expected.pdf.0.png b/testing/resources/pixel/bug_524043_4_expected.pdf.0.png
index eabf957..a7e1fd7 100644
--- a/testing/resources/pixel/bug_524043_4_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_524043_4_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_524043_4_expected_agg_mac.pdf.0.png b/testing/resources/pixel/bug_524043_4_expected_agg_mac.pdf.0.png
new file mode 100644
index 0000000..e794355
--- /dev/null
+++ b/testing/resources/pixel/bug_524043_4_expected_agg_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_524043_4_expected_mac.pdf.0.png b/testing/resources/pixel/bug_524043_4_expected_mac.pdf.0.png
deleted file mode 100644
index b85b9d0..0000000
--- a/testing/resources/pixel/bug_524043_4_expected_mac.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_524043_4_expected_win.pdf.0.png b/testing/resources/pixel/bug_524043_4_expected_win.pdf.0.png
deleted file mode 100644
index b2c134a..0000000
--- a/testing/resources/pixel/bug_524043_4_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_524043_5_expected.pdf.0.png b/testing/resources/pixel/bug_524043_5_expected.pdf.0.png
index eabf957..a7e1fd7 100644
--- a/testing/resources/pixel/bug_524043_5_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_524043_5_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_524043_5_expected_agg_mac.pdf.0.png b/testing/resources/pixel/bug_524043_5_expected_agg_mac.pdf.0.png
new file mode 100644
index 0000000..e794355
--- /dev/null
+++ b/testing/resources/pixel/bug_524043_5_expected_agg_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_524043_5_expected_mac.pdf.0.png b/testing/resources/pixel/bug_524043_5_expected_mac.pdf.0.png
deleted file mode 100644
index b85b9d0..0000000
--- a/testing/resources/pixel/bug_524043_5_expected_mac.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_524043_5_expected_win.pdf.0.png b/testing/resources/pixel/bug_524043_5_expected_win.pdf.0.png
deleted file mode 100644
index b2c134a..0000000
--- a/testing/resources/pixel/bug_524043_5_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_524043_6_expected_mac.pdf.0.png b/testing/resources/pixel/bug_524043_6_expected_mac.pdf.0.png
deleted file mode 100644
index 3edcc2d..0000000
--- a/testing/resources/pixel/bug_524043_6_expected_mac.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_524043_7_expected.pdf.0.png b/testing/resources/pixel/bug_524043_7_expected.pdf.0.png
index 04304fe..4dd2672 100644
--- a/testing/resources/pixel/bug_524043_7_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_524043_7_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_524043_7_expected_agg_mac.pdf.0.png b/testing/resources/pixel/bug_524043_7_expected_agg_mac.pdf.0.png
new file mode 100644
index 0000000..e3bf21f
--- /dev/null
+++ b/testing/resources/pixel/bug_524043_7_expected_agg_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_524043_7_expected_mac.pdf.0.png b/testing/resources/pixel/bug_524043_7_expected_mac.pdf.0.png
deleted file mode 100644
index ecede7d..0000000
--- a/testing/resources/pixel/bug_524043_7_expected_mac.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_524043_7_expected_win.pdf.0.png b/testing/resources/pixel/bug_524043_7_expected_win.pdf.0.png
deleted file mode 100644
index 786d963..0000000
--- a/testing/resources/pixel/bug_524043_7_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_528103_expected.pdf.0.png b/testing/resources/pixel/bug_528103_expected.pdf.0.png
index 5c0c254..79240dd 100644
--- a/testing/resources/pixel/bug_528103_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_528103_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_528103_expected_agg_mac.pdf.0.png b/testing/resources/pixel/bug_528103_expected_agg_mac.pdf.0.png
new file mode 100644
index 0000000..0d23b55
--- /dev/null
+++ b/testing/resources/pixel/bug_528103_expected_agg_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_528103_expected_mac.pdf.0.png b/testing/resources/pixel/bug_528103_expected_mac.pdf.0.png
deleted file mode 100644
index cb028f9..0000000
--- a/testing/resources/pixel/bug_528103_expected_mac.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_528103_expected_win.pdf.0.png b/testing/resources/pixel/bug_528103_expected_win.pdf.0.png
deleted file mode 100644
index 8ccda23..0000000
--- a/testing/resources/pixel/bug_528103_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_543018_1_expected.pdf.0.png b/testing/resources/pixel/bug_543018_1_expected.pdf.0.png
index eabf957..a7e1fd7 100644
--- a/testing/resources/pixel/bug_543018_1_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_543018_1_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_543018_1_expected_agg_mac.pdf.0.png b/testing/resources/pixel/bug_543018_1_expected_agg_mac.pdf.0.png
new file mode 100644
index 0000000..e794355
--- /dev/null
+++ b/testing/resources/pixel/bug_543018_1_expected_agg_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_543018_1_expected_mac.pdf.0.png b/testing/resources/pixel/bug_543018_1_expected_mac.pdf.0.png
deleted file mode 100644
index b85b9d0..0000000
--- a/testing/resources/pixel/bug_543018_1_expected_mac.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_543018_1_expected_win.pdf.0.png b/testing/resources/pixel/bug_543018_1_expected_win.pdf.0.png
deleted file mode 100644
index b2c134a..0000000
--- a/testing/resources/pixel/bug_543018_1_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_543018_2_expected.pdf.0.png b/testing/resources/pixel/bug_543018_2_expected.pdf.0.png
index eabf957..a7e1fd7 100644
--- a/testing/resources/pixel/bug_543018_2_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_543018_2_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_543018_2_expected_agg_mac.pdf.0.png b/testing/resources/pixel/bug_543018_2_expected_agg_mac.pdf.0.png
new file mode 100644
index 0000000..e794355
--- /dev/null
+++ b/testing/resources/pixel/bug_543018_2_expected_agg_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_543018_2_expected_mac.pdf.0.png b/testing/resources/pixel/bug_543018_2_expected_mac.pdf.0.png
deleted file mode 100644
index b85b9d0..0000000
--- a/testing/resources/pixel/bug_543018_2_expected_mac.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_543018_2_expected_win.pdf.0.png b/testing/resources/pixel/bug_543018_2_expected_win.pdf.0.png
deleted file mode 100644
index b2c134a..0000000
--- a/testing/resources/pixel/bug_543018_2_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_551258_1_expected.pdf.0.png b/testing/resources/pixel/bug_551258_1_expected.pdf.0.png
index eabf957..a7e1fd7 100644
--- a/testing/resources/pixel/bug_551258_1_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_551258_1_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_551258_1_expected_agg_mac.pdf.0.png b/testing/resources/pixel/bug_551258_1_expected_agg_mac.pdf.0.png
new file mode 100644
index 0000000..e794355
--- /dev/null
+++ b/testing/resources/pixel/bug_551258_1_expected_agg_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_551258_1_expected_mac.pdf.0.png b/testing/resources/pixel/bug_551258_1_expected_mac.pdf.0.png
deleted file mode 100644
index b85b9d0..0000000
--- a/testing/resources/pixel/bug_551258_1_expected_mac.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_551258_1_expected_win.pdf.0.png b/testing/resources/pixel/bug_551258_1_expected_win.pdf.0.png
deleted file mode 100644
index b2c134a..0000000
--- a/testing/resources/pixel/bug_551258_1_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_554151_expected.pdf.0.png b/testing/resources/pixel/bug_554151_expected.pdf.0.png
index 08c11b0..5b99a4d 100644
--- a/testing/resources/pixel/bug_554151_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_554151_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_557223.in b/testing/resources/pixel/bug_557223.in
index acb1ff2..c0c06b6 100644
--- a/testing/resources/pixel/bug_557223.in
+++ b/testing/resources/pixel/bug_557223.in
@@ -75,7 +75,7 @@
trailer <<
/Info 6 0 R
/Root 1 0 R
- /Size 7
+ {{trailersize}}
>>
{{startxref}}
%%EOF
diff --git a/testing/resources/pixel/bug_585_expected_skia.pdf.0.png b/testing/resources/pixel/bug_585_expected_skia.pdf.0.png
new file mode 100644
index 0000000..c49e21e
--- /dev/null
+++ b/testing/resources/pixel/bug_585_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_591137_expected.pdf.0.png b/testing/resources/pixel/bug_591137_expected.pdf.0.png
index 265e682..74c5c64 100644
--- a/testing/resources/pixel/bug_591137_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_591137_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_601362_expected.pdf.0.png b/testing/resources/pixel/bug_601362_expected.pdf.0.png
index 5d868d5..49db8d9 100644
--- a/testing/resources/pixel/bug_601362_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_601362_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_601362_expected_skia.pdf.0.png b/testing/resources/pixel/bug_601362_expected_skia.pdf.0.png
new file mode 100644
index 0000000..d371b3a
--- /dev/null
+++ b/testing/resources/pixel/bug_601362_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_632_expected.pdf.0.png b/testing/resources/pixel/bug_632_expected.pdf.0.png
index 3807d93..4fc6081 100644
--- a/testing/resources/pixel/bug_632_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_632_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_632_expected.pdf.1.png b/testing/resources/pixel/bug_632_expected.pdf.1.png
index 141f3d8..ee11ce6 100644
--- a/testing/resources/pixel/bug_632_expected.pdf.1.png
+++ b/testing/resources/pixel/bug_632_expected.pdf.1.png
Binary files differ
diff --git a/testing/resources/pixel/bug_632_expected_skia.pdf.1.png b/testing/resources/pixel/bug_632_expected_skia.pdf.1.png
new file mode 100644
index 0000000..002dcba
--- /dev/null
+++ b/testing/resources/pixel/bug_632_expected_skia.pdf.1.png
Binary files differ
diff --git a/testing/resources/pixel/bug_660850_expected_skia.pdf.0.png b/testing/resources/pixel/bug_660850_expected_skia.pdf.0.png
new file mode 100644
index 0000000..254f45b
--- /dev/null
+++ b/testing/resources/pixel/bug_660850_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_665467.in b/testing/resources/pixel/bug_665467.in
index 6ef2c1c..289e8de 100644
--- a/testing/resources/pixel/bug_665467.in
+++ b/testing/resources/pixel/bug_665467.in
@@ -4,96 +4,66 @@
/Pages 2 0 R
>>
endobj
-
{{object 2 0}} <<
/Type /Pages
- /MediaBox [ 0 0 100 100 ]
/Count 1
- /Kids [ 3 0 R ]
+ /Kids [3 0 R]
+ /MediaBox [0 0 100 100]
>>
endobj
-
{{object 3 0}} <<
/Type /Page
/Parent 2 0 R
- /Resources
- <<
- /Font << /F1 4 0 R >>
+ /Contents 4 0 R
+ /Resources <<
+ /Font <<
+ /F1 5 0 R
+ >>
>>
- /Contents 8 0 R
>>
endobj
-
{{object 4 0}} <<
- /Type /Font
- /Subtype /TrueType
- /BaseFont /ChromeSansMM
- /Encoding 5 0 R
- /FirstChar 32
- /LastChar 255
- /Name /F1
- /ToUnicode 6 0 R
- /FontDescriptor 7 0 R
->>
-endobj
-
-{{object 5 0}} <<
- /Differences [ 161 /someunknownname ]
- /Type /Encoding
->>
-endobj
-
-{{object 6 0}} <<
->>
-stream
-/CIDInit /ProcSet findresource begin
-12 dict begin
-begincmap
-/CIDSystemInfo
-<</Registry (Adobe)
-/Ordering (Identity)
-/Supplement 0
->> def
-/CMapName /Adobe-Identity-H def
-CMapType 2 def
-1 begincodespacerange
-<00> <FF>
-endcodespacerange
-1 beginbfchar
-<A1> <043B>
-endbfchar
-endcmap
-CMapName currentdict /CMap defineresource pop
-end
-end
-endstream
-endobj
-
-{{object 7 0}} <<
- << /Ascent 1000
- /CapHeight 0
- /Descent -200
- /Flags 32
- /FontBBox [ -599 -207 1338 1034 ]
- /FontName /ChromeSansMM
- /ItalicAngle 0
- /StemV 0
- /Type /FontDescriptor
->>
-endobj
-
-{{object 8 0}} <<
+ {{streamlen}}
>>
stream
BT
-50 50 Td /F1 15 Tf <A1> Tj
+50 50 Td
+/F1 15 Tf
+[<8C>]TJ
ET
endstream
endobj
-
-{{xref}}
-trailer <<
- /Root 1 0 R
+{{object 5 0}} <<
+ /Type /Font
+ /Subtype /TrueType
+ /BaseFont /Tahoma
+ /Encoding 6 0 R
+ /FirstChar 139
+ /FontDescriptor 7 0 R
+ /LastChar 140
+ /Widths [943 677]
>>
+endobj
+{{object 6 0}} <<
+ /Type /Encoding
+ /Differences [
+ 140 /Elcyrillic
+ ]
+>>
+endobj
+{{object 7 0}} <<
+ /Type /FontDescriptor
+ /Ascent 1034
+ /CapHeight 0
+ /Descent -207
+ /Flags 32
+ /FontBBox [-599 -207 1338 1034]
+ /FontName /Tahoma
+ /ItalicAngle 0
+ /StemV 0
+>>
+endobj
+{{xref}}
+{{trailer}}
{{startxref}}
%%EOF
diff --git a/testing/resources/pixel/bug_665467_expected.pdf.0.png b/testing/resources/pixel/bug_665467_expected.pdf.0.png
index e3b37b4..4532565 100644
--- a/testing/resources/pixel/bug_665467_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_665467_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_665467_expected_mac.pdf.0.png b/testing/resources/pixel/bug_665467_expected_mac.pdf.0.png
index 9f51dcb..0faddf8 100644
--- a/testing/resources/pixel/bug_665467_expected_mac.pdf.0.png
+++ b/testing/resources/pixel/bug_665467_expected_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_665467_expected_skia.pdf.0.png b/testing/resources/pixel/bug_665467_expected_skia.pdf.0.png
new file mode 100644
index 0000000..2cd13ff
--- /dev/null
+++ b/testing/resources/pixel/bug_665467_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_665467_expected_win.pdf.0.png b/testing/resources/pixel/bug_665467_expected_win.pdf.0.png
deleted file mode 100644
index 8feb717..0000000
--- a/testing/resources/pixel/bug_665467_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_714187_expected_skia.pdf.0.png b/testing/resources/pixel/bug_714187_expected_skia.pdf.0.png
new file mode 100644
index 0000000..80cac57
--- /dev/null
+++ b/testing/resources/pixel/bug_714187_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_725389.in b/testing/resources/pixel/bug_725389.in
new file mode 100644
index 0000000..0d0818b
--- /dev/null
+++ b/testing/resources/pixel/bug_725389.in
@@ -0,0 +1,58 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /AcroForm <<
+ /Fields [4 0 R]
+ >>
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Annots [4 0 R]
+ /Parent 2 0 R
+ /MediaBox [0 0 200 100]
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Tx
+ /BS <<
+ /S /S
+ /W 1
+ >>
+ /DA (/Times-Roman 12 Tf 0 0 0 rg)
+ /DR <<
+ /Font <<
+ /Times-Roman 5 0 R
+ >>
+ >>
+ /DV ()
+ /F 4
+ /Ff 0
+ /MK <<
+ /BG [1 1 1]
+ >>
+ /P 3 0 R
+ /T (Default)
+ /V (\376\377\5\321\5\327\5\350\0.\0.\0.)
+ /Rect [50 40 150 70]
+>>
+endobj
+{{object 5 0}} <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Times-Roman
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_725389_expected.pdf.0.png b/testing/resources/pixel/bug_725389_expected.pdf.0.png
new file mode 100644
index 0000000..6da1b77
--- /dev/null
+++ b/testing/resources/pixel/bug_725389_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_725389_expected_mac.pdf.0.png b/testing/resources/pixel/bug_725389_expected_mac.pdf.0.png
new file mode 100644
index 0000000..453c2c3
--- /dev/null
+++ b/testing/resources/pixel/bug_725389_expected_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_725389_expected_skia.pdf.0.png b/testing/resources/pixel/bug_725389_expected_skia.pdf.0.png
new file mode 100644
index 0000000..25ad890
--- /dev/null
+++ b/testing/resources/pixel/bug_725389_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_725389_expected_skia_mac.pdf.0.png b/testing/resources/pixel/bug_725389_expected_skia_mac.pdf.0.png
new file mode 100644
index 0000000..4a5a4b0
--- /dev/null
+++ b/testing/resources/pixel/bug_725389_expected_skia_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_733528_expected.pdf.0.png b/testing/resources/pixel/bug_733528_expected.pdf.0.png
index eaf48b5..5e178da 100644
--- a/testing/resources/pixel/bug_733528_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_733528_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_733528_expected_mac.pdf.0.png b/testing/resources/pixel/bug_733528_expected_mac.pdf.0.png
index f7c8302..992e488 100644
--- a/testing/resources/pixel/bug_733528_expected_mac.pdf.0.png
+++ b/testing/resources/pixel/bug_733528_expected_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_733528_expected_skia.pdf.0.png b/testing/resources/pixel/bug_733528_expected_skia.pdf.0.png
new file mode 100644
index 0000000..6dfa99d
--- /dev/null
+++ b/testing/resources/pixel/bug_733528_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_733528_expected_win.pdf.0.png b/testing/resources/pixel/bug_733528_expected_win.pdf.0.png
deleted file mode 100644
index a44b932..0000000
--- a/testing/resources/pixel/bug_733528_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_736695_1_expected.pdf.0.png b/testing/resources/pixel/bug_736695_1_expected.pdf.0.png
index 99d7bd7..10bbb0d 100644
--- a/testing/resources/pixel/bug_736695_1_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_736695_1_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_736695_1_expected_skia.pdf.0.png b/testing/resources/pixel/bug_736695_1_expected_skia.pdf.0.png
new file mode 100644
index 0000000..ecb7d2b
--- /dev/null
+++ b/testing/resources/pixel/bug_736695_1_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_736695_2_expected.pdf.0.png b/testing/resources/pixel/bug_736695_2_expected.pdf.0.png
index 33ea077..4fe962d 100644
--- a/testing/resources/pixel/bug_736695_2_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_736695_2_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_736695_2_expected_mac.pdf.0.png b/testing/resources/pixel/bug_736695_2_expected_mac.pdf.0.png
index 46c7085..e46e02d 100644
--- a/testing/resources/pixel/bug_736695_2_expected_mac.pdf.0.png
+++ b/testing/resources/pixel/bug_736695_2_expected_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_736695_2_expected_skia.pdf.0.png b/testing/resources/pixel/bug_736695_2_expected_skia.pdf.0.png
new file mode 100644
index 0000000..801b157
--- /dev/null
+++ b/testing/resources/pixel/bug_736695_2_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_736695_2_expected_win.pdf.0.png b/testing/resources/pixel/bug_736695_2_expected_win.pdf.0.png
deleted file mode 100644
index ffa94f0..0000000
--- a/testing/resources/pixel/bug_736695_2_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_736695_3_expected.pdf.0.png b/testing/resources/pixel/bug_736695_3_expected.pdf.0.png
index cbffe0a..13c9d0a 100644
--- a/testing/resources/pixel/bug_736695_3_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_736695_3_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_736695_3_expected_mac.pdf.0.png b/testing/resources/pixel/bug_736695_3_expected_mac.pdf.0.png
index 1abcd18..f15d038 100644
--- a/testing/resources/pixel/bug_736695_3_expected_mac.pdf.0.png
+++ b/testing/resources/pixel/bug_736695_3_expected_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_736695_3_expected_skia.pdf.0.png b/testing/resources/pixel/bug_736695_3_expected_skia.pdf.0.png
new file mode 100644
index 0000000..1b02f1b
--- /dev/null
+++ b/testing/resources/pixel/bug_736695_3_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_736695_3_expected_win.pdf.0.png b/testing/resources/pixel/bug_736695_3_expected_win.pdf.0.png
deleted file mode 100644
index d27a9b4..0000000
--- a/testing/resources/pixel/bug_736695_3_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_736695_4_expected.pdf.0.png b/testing/resources/pixel/bug_736695_4_expected.pdf.0.png
index 99d7bd7..10bbb0d 100644
--- a/testing/resources/pixel/bug_736695_4_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_736695_4_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_736695_4_expected_skia.pdf.0.png b/testing/resources/pixel/bug_736695_4_expected_skia.pdf.0.png
new file mode 100644
index 0000000..ecb7d2b
--- /dev/null
+++ b/testing/resources/pixel/bug_736695_4_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_736703.in b/testing/resources/pixel/bug_736703.in
new file mode 100644
index 0000000..05c4a35
--- /dev/null
+++ b/testing/resources/pixel/bug_736703.in
@@ -0,0 +1,73 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /Resources <<
+ /ProcSet [/PDF /Text]
+ /Font <<
+ /F4 5 0 R
+ >>
+ >>
+ /MediaBox [0 0 200 200]
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+BT
+/F4 30 Tf
+0 0 0 rg
+1 0 0 1 30 100 Tm
+[(honorably)]TJ
+ET
+Q
+endstream
+endobj
+{{object 5 0}} <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /AGaramond-Regular
+ /Encoding /WinAnsiEncoding
+ /FirstChar 32
+ /FontDescriptor 6 0 R
+ /LastChar 125
+ /Widths [250 220 404 500 500 844 818 235 320 320 394 500 250 320 250 327 500
+ 500 500 500 500 500 500 500 500 500 250 250 500 500 500 321 765 623 605 696
+ 780 584 538 747 806 338 345 675 553 912 783 795 549 795 645 489 660 746 676
+ 960 643 574 641 320 309 320 500 500 360 404 500 400 509 396 290 446 515 257
+ 253 482 247 787 525 486 507 497 332 323 307 512 432 660 432 438 377 320 239
+ 320]
+>>
+endobj
+{{object 6 0}} <<
+ /Type /FontDescriptor
+ /Ascent 720
+ /CapHeight 663
+ /Descent -270
+ /Flags 34
+ /FontBBox [-183 -269 1099 851]
+ /FontName /AGaramond-Regular
+ /ItalicAngle 0
+ /StemH 40
+ /StemV 74
+ /XHeight 397
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_736703_expected.pdf.0.png b/testing/resources/pixel/bug_736703_expected.pdf.0.png
new file mode 100644
index 0000000..f2fe3a1
--- /dev/null
+++ b/testing/resources/pixel/bug_736703_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_820345_expected.pdf.0.png b/testing/resources/pixel/bug_820345_expected.pdf.0.png
index e3c8e24..e89a680 100644
--- a/testing/resources/pixel/bug_820345_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_820345_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_820345_expected_agg_mac.pdf.0.png b/testing/resources/pixel/bug_820345_expected_agg_mac.pdf.0.png
new file mode 100644
index 0000000..6013c5c
--- /dev/null
+++ b/testing/resources/pixel/bug_820345_expected_agg_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_820345_expected_mac.pdf.0.png b/testing/resources/pixel/bug_820345_expected_mac.pdf.0.png
deleted file mode 100644
index 6e79402..0000000
--- a/testing/resources/pixel/bug_820345_expected_mac.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_820345_expected_win.pdf.0.png b/testing/resources/pixel/bug_820345_expected_win.pdf.0.png
deleted file mode 100644
index c3b8b43..0000000
--- a/testing/resources/pixel/bug_820345_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_842.in b/testing/resources/pixel/bug_842.in
index 255a902..71f0104 100644
--- a/testing/resources/pixel/bug_842.in
+++ b/testing/resources/pixel/bug_842.in
@@ -24,7 +24,7 @@
>>
endobj
{{object 4 0}} <<
-{{streamlen}}
+ {{streamlen}}
>>
stream
q
diff --git a/testing/resources/pixel/bug_842_expected_skia.pdf.0.png b/testing/resources/pixel/bug_842_expected_skia.pdf.0.png
new file mode 100644
index 0000000..eb10aa7
--- /dev/null
+++ b/testing/resources/pixel/bug_842_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_843_expected_skia.pdf.0.png b/testing/resources/pixel/bug_843_expected_skia.pdf.0.png
new file mode 100644
index 0000000..f0c8193
--- /dev/null
+++ b/testing/resources/pixel/bug_843_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_845697_expected.pdf.0.png b/testing/resources/pixel/bug_845697_expected.pdf.0.png
index 3bc224b..15ec4dd 100644
--- a/testing/resources/pixel/bug_845697_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_845697_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_846.in b/testing/resources/pixel/bug_846.in
new file mode 100644
index 0000000..e405d67
--- /dev/null
+++ b/testing/resources/pixel/bug_846.in
@@ -0,0 +1,160 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Resources 4 0 R
+ /Contents 15 0 R
+ /MediaBox [0 0 200 200]
+>>
+endobj
+{{object 4 0}} <<
+ /ProcSet [/PDF /Text]
+ /Font <<
+ /F1 5 0 R
+ /F2 7 0 R
+ /F3 9 0 R
+ /F4 11 0 R
+ >>
+>>
+endobj
+{{object 5 0}} <<
+ /Type /Font
+ /Subtype /TrueType
+ /BaseFont /Arial-Bold
+ /Encoding /WinAnsiEncoding
+ /FirstChar 65
+ /FontDescriptor 6 0 R
+ /LastChar 65
+ /Widths [778]
+>>
+endobj
+{{object 6 0}} <<
+ /Type /FontDescriptor
+ /Ascent 923
+ /CapHeight 687
+ /Descent -282
+ /Flags 34
+ /FontName /Arial-Bold
+ /FontBBox 14 0 R
+ /ItalicAngle 0
+ /StemV 140.269
+ /XHeight 0
+>>
+endobj
+{{object 7 0}} <<
+ /Type /Font
+ /Subtype /TrueType
+ /BaseFont /Arial-Bold,Bold
+ /Encoding /WinAnsiEncoding
+ /FirstChar 65
+ /FontDescriptor 8 0 R
+ /LastChar 65
+ /Widths [778]
+>>
+endobj
+{{object 8 0}} <<
+ /Type /FontDescriptor
+ /Ascent 923
+ /CapHeight 687
+ /Descent -282
+ /Flags 34
+ /FontName /Arial-Bold,Bold
+ /FontBBox 14 0 R
+ /ItalicAngle 0
+ /StemV 140.269
+ /XHeight 0
+>>
+endobj
+{{object 9 0}} <<
+ /Type /Font
+ /Subtype /TrueType
+ /BaseFont /DejaVuSans-Bold
+ /Encoding /WinAnsiEncoding
+ /FirstChar 58
+ /FontDescriptor 10 0 R
+ /LastChar 65
+ /Widths 13 0 R
+>>
+endobj
+{{object 10 0}} <<
+ /Type /FontDescriptor
+ /Ascent 923
+ /CapHeight 687
+ /Descent -282
+ /Flags 34
+ /FontName /DejaVuSans-Bold
+ /FontBBox 14 0 R
+ /ItalicAngle 0
+ /StemV 140.269
+ /XHeight 0
+>>
+endobj
+{{object 11 0}} <<
+ /Type /Font
+ /Subtype /TrueType
+ /BaseFont /DejaVuSans-Bold,Bold
+ /Encoding /WinAnsiEncoding
+ /FirstChar 58
+ /FontDescriptor 12 0 R
+ /LastChar 65
+ /Widths 13 0 R
+>>
+endobj
+{{object 12 0}} <<
+ /Type /FontDescriptor
+ /Ascent 923
+ /CapHeight 687
+ /Descent -282
+ /Flags 34
+ /FontName /DejaVuSans-Bold,Bold
+ /FontBBox 14 0 R
+ /ItalicAngle 0
+ /StemV 140.269
+ /XHeight 0
+>>
+endobj
+{{object 13 0}} [250 0 0 0 0 0 0 778]
+endobj
+{{object 14 0}} [-134 -337 1193 1021]
+endobj
+{{object 15 0}} <<
+ {{streamlen}}
+>>
+stream
+BT
+/F1 2 Tf
+18 0 0 18 50 120 Tm
+(A)Tj
+ET
+BT
+/F2 2 Tf
+18 0 0 18 120 120 Tm
+(A)Tj
+ET
+BT
+/F3 2 Tf
+18 0 0 18 50 60 Tm
+(A)Tj
+ET
+BT
+/F4 2 Tf
+18 0 0 18 120 60 Tm
+(A)Tj
+ET
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_846_expected.pdf.0.png b/testing/resources/pixel/bug_846_expected.pdf.0.png
new file mode 100644
index 0000000..1ff3944
--- /dev/null
+++ b/testing/resources/pixel/bug_846_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_846_expected_mac.pdf.0.png b/testing/resources/pixel/bug_846_expected_mac.pdf.0.png
new file mode 100644
index 0000000..b405dce
--- /dev/null
+++ b/testing/resources/pixel/bug_846_expected_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_846_expected_skia.pdf.0.png b/testing/resources/pixel/bug_846_expected_skia.pdf.0.png
new file mode 100644
index 0000000..f7fa9ff
--- /dev/null
+++ b/testing/resources/pixel/bug_846_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_846_expected_skia_mac.pdf.0.png b/testing/resources/pixel/bug_846_expected_skia_mac.pdf.0.png
new file mode 100644
index 0000000..5635527
--- /dev/null
+++ b/testing/resources/pixel/bug_846_expected_skia_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_846_expected_skia_win.pdf.0.png b/testing/resources/pixel/bug_846_expected_skia_win.pdf.0.png
new file mode 100644
index 0000000..5635527
--- /dev/null
+++ b/testing/resources/pixel/bug_846_expected_skia_win.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_909762_expected.pdf.0.png b/testing/resources/pixel/bug_909762_expected.pdf.0.png
index 219c163..d01a7fc 100644
--- a/testing/resources/pixel/bug_909762_expected.pdf.0.png
+++ b/testing/resources/pixel/bug_909762_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_909762_expected_win.pdf.0.png b/testing/resources/pixel/bug_909762_expected_win.pdf.0.png
deleted file mode 100644
index c6d2727..0000000
--- a/testing/resources/pixel/bug_909762_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_925736_expected_agg_mac.pdf.0.png b/testing/resources/pixel/bug_925736_expected_agg_mac.pdf.0.png
new file mode 100644
index 0000000..1763451
--- /dev/null
+++ b/testing/resources/pixel/bug_925736_expected_agg_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_925736_expected_mac.pdf.0.png b/testing/resources/pixel/bug_925736_expected_mac.pdf.0.png
deleted file mode 100644
index f71efde..0000000
--- a/testing/resources/pixel/bug_925736_expected_mac.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_966263.in b/testing/resources/pixel/bug_966263.in
new file mode 100644
index 0000000..bddf328
--- /dev/null
+++ b/testing/resources/pixel/bug_966263.in
@@ -0,0 +1,132 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 1 0 R
+ /Contents 4 0 R
+ /MediaBox [0 0 200 400]
+ /Resources <<
+ /ProcSet [/PDF /Text]
+ /Font <<
+ /F1 5 0 R
+ >>
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+BT
+/F1 1 Tf
+1 0 0 1 20 55 Tm
+(ABCD)Tj
+ET
+endstream
+endobj
+{{object 5 0}} <<
+ /Type /Font
+ /Subtype /Type3
+ /FontBBox [0 0 600 600]
+ /FontMatrix [1 0 0 -100 0 0]
+ /CharProcs <<
+ /CA 6 0 R
+ /CB 7 0 R
+ /CC 8 0 R
+ /CD 9 0 R
+ >>
+ /Encoding <<
+ /Type /Encoding
+ /Differences [65 /CA 66 /CB 67 /CC 68 /CD]
+ >>
+ /FirstChar 65
+ /LastChar 68
+ /Widths [18 1000 18 18]
+ /Resources <<
+ /ProcSet [/PDF /ImageB]
+ >>
+>>
+endobj
+{{object 6 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+d0 0 0
+1 0 0 1 6 0 cm
+BI
+/W 1
+/H 1
+/BPC 1
+/IM true
+ID
+x
+EI
+Q
+endstream
+endobj
+{{object 7 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+6 0 0 48 6 0 cm
+BI
+/W 6
+/H 48
+/BPC 1
+/IM true
+ID
+xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+EI
+Q
+endstream
+endobj
+{{object 8 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+6 0 0 1 2147483570 0 cm
+BI
+/W 6
+/H 1
+/BPC 1
+/IM true
+ID
+x
+EI
+Q
+endstream
+endobj
+{{object 9 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+1 0 0 6 0 21474834 cm
+BI
+/W 1
+/H 1
+/BPC 1
+/IM true
+ID
+x
+EI
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_966263_expected.pdf.0.png b/testing/resources/pixel/bug_966263_expected.pdf.0.png
new file mode 100644
index 0000000..84650b6
--- /dev/null
+++ b/testing/resources/pixel/bug_966263_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_972999.in b/testing/resources/pixel/bug_972999.in
new file mode 100644
index 0000000..6e928da
--- /dev/null
+++ b/testing/resources/pixel/bug_972999.in
@@ -0,0 +1,603 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /MediaBox [0 0 200 300]
+ /Resources <<
+ /XObject <<
+ /X1 5 0 R
+ >>
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 5 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /BBox [0 0 200 300]
+ /Resources <<
+ /XObject <<
+ /X1 6 0 R
+ >>
+ >>
+ {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 6 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /BBox [0 0 200 300]
+ /Resources <<
+ /XObject <<
+ /X1 7 0 R
+ >>
+ >>
+ {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 7 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /BBox [0 0 200 300]
+ /Resources <<
+ /XObject <<
+ /X1 8 0 R
+ >>
+ >>
+ {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 8 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /BBox [0 0 200 300]
+ /Resources <<
+ /XObject <<
+ /X1 9 0 R
+ >>
+ >>
+ {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 9 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /BBox [0 0 200 300]
+ /Resources <<
+ /XObject <<
+ /X1 10 0 R
+ >>
+ >>
+ {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 10 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /BBox [0 0 200 300]
+ /Resources <<
+ /XObject <<
+ /X1 11 0 R
+ >>
+ >>
+ {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 11 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /BBox [0 0 200 300]
+ /Resources <<
+ /XObject <<
+ /X1 12 0 R
+ >>
+ >>
+ {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 12 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /BBox [0 0 200 300]
+ /Resources <<
+ /XObject <<
+ /X1 13 0 R
+ >>
+ >>
+ {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 13 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /BBox [0 0 200 300]
+ /Resources <<
+ /XObject <<
+ /X1 14 0 R
+ >>
+ >>
+ {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 14 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /BBox [0 0 200 300]
+ /Resources <<
+ /XObject <<
+ /X1 15 0 R
+ >>
+ >>
+ {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 15 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /BBox [0 0 200 300]
+ /Resources <<
+ /XObject <<
+ /X1 16 0 R
+ >>
+ >>
+ {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 16 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /BBox [0 0 200 300]
+ /Resources <<
+ /XObject <<
+ /X1 17 0 R
+ >>
+ >>
+ {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 17 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /BBox [0 0 200 300]
+ /Resources <<
+ /XObject <<
+ /X1 18 0 R
+ >>
+ >>
+ {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 18 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /BBox [0 0 200 300]
+ /Resources <<
+ /XObject <<
+ /X1 19 0 R
+ >>
+ >>
+ {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 19 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /BBox [0 0 200 300]
+ /Resources <<
+ /XObject <<
+ /X1 20 0 R
+ >>
+ >>
+ {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 20 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /BBox [0 0 200 300]
+ /Resources <<
+ /XObject <<
+ /X1 21 0 R
+ >>
+ >>
+ {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 21 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /BBox [0 0 200 300]
+ /Resources <<
+ /XObject <<
+ /X1 22 0 R
+ >>
+ >>
+ {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 22 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /BBox [0 0 200 300]
+ /Resources <<
+ /XObject <<
+ /X1 23 0 R
+ >>
+ >>
+ {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 23 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /BBox [0 0 200 300]
+ /Resources <<
+ /XObject <<
+ /X1 24 0 R
+ >>
+ >>
+ {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 24 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /BBox [0 0 200 300]
+ /Resources <<
+ /XObject <<
+ /X1 25 0 R
+ >>
+ >>
+ {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 25 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /BBox [0 0 200 300]
+ /Resources <<
+ /XObject <<
+ /X1 26 0 R
+ >>
+ >>
+ {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 26 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /BBox [0 0 200 300]
+ /Resources <<
+ /XObject <<
+ /X1 27 0 R
+ >>
+ >>
+ {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 27 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /BBox [0 0 200 300]
+ /Resources <<
+ /XObject <<
+ /X1 28 0 R
+ >>
+ >>
+ {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 28 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /BBox [0 0 200 300]
+ /Resources <<
+ /XObject <<
+ /X1 29 0 R
+ >>
+ >>
+ {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 29 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /BBox [0 0 200 300]
+ /Resources <<
+ /XObject <<
+ /X1 30 0 R
+ >>
+ >>
+ {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 30 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /BBox [0 0 200 300]
+ /Resources <<
+ /XObject <<
+ /X1 31 0 R
+ >>
+ >>
+ {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 31 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /BBox [0 0 200 300]
+ /Resources <<
+ /XObject <<
+ /X1 32 0 R
+ >>
+ >>
+ {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 32 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /BBox [0 0 200 300]
+ /Resources <<
+ /XObject <<
+ /X1 33 0 R
+ >>
+ >>
+ {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 33 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /BBox [0 0 200 300]
+ /Resources <<
+ /XObject <<
+ /X1 34 0 R
+ >>
+ >>
+ {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 34 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /BBox [0 0 200 300]
+ /Resources <<
+ /XObject <<
+ /X1 35 0 R
+ >>
+ >>
+ {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 35 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /BBox [0 0 200 300]
+ /Resources <<
+ /XObject <<
+ /X1 36 0 R
+ >>
+ >>
+ {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 36 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /BBox [0 0 200 300]
+ /Resources <<
+ /XObject <<
+ /X1 37 0 R
+ >>
+ >>
+ {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 37 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /BBox [0 0 200 300]
+ /Resources <<
+ /XObject <<
+ /X1 38 0 R
+ >>
+ >>
+ {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 38 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /BBox [0 0 200 300]
+ /Resources <<
+ /XObject <<
+ /X1 39 0 R
+ >>
+ >>
+ {{streamlen}}
+>>
+stream
+/X1 Do
+endstream
+endobj
+{{object 39 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /BBox [0 0 200 300]
+ {{streamlen}}
+>>
+stream
+q
+0 0 0 rg
+0 290 10 10 re B*
+10 150 50 30 re B*
+0 0 1 rg
+190 290 10 10 re B*
+70 232 50 30 re B*
+0 1 0 rg
+190 0 10 10 re B*
+130 150 50 30 re B*
+1 0 0 rg
+0 0 10 10 re B*
+70 67 50 30 re B*
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_972999_expected.pdf.0.png b/testing/resources/pixel/bug_972999_expected.pdf.0.png
new file mode 100644
index 0000000..ee652fa
--- /dev/null
+++ b/testing/resources/pixel/bug_972999_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_972999_expected_skia.pdf.0.png b/testing/resources/pixel/bug_972999_expected_skia.pdf.0.png
new file mode 100644
index 0000000..8a6b8ed
--- /dev/null
+++ b/testing/resources/pixel/bug_972999_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_983289.in b/testing/resources/pixel/bug_983289.in
new file mode 100644
index 0000000..438af5d
--- /dev/null
+++ b/testing/resources/pixel/bug_983289.in
@@ -0,0 +1,35 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 612 792]
+ /Contents 4 0 R
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+2 0 0 2 0 0 cm
+-10926 -22396 m
+11272 23097 l
+S
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_983289_expected.pdf.0.png b/testing/resources/pixel/bug_983289_expected.pdf.0.png
new file mode 100644
index 0000000..27049d5
--- /dev/null
+++ b/testing/resources/pixel/bug_983289_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_984811_expected_agg_mac.pdf.0.png b/testing/resources/pixel/bug_984811_expected_agg_mac.pdf.0.png
new file mode 100644
index 0000000..4a217fa
--- /dev/null
+++ b/testing/resources/pixel/bug_984811_expected_agg_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/bug_984811_expected_mac.pdf.0.png b/testing/resources/pixel/bug_984811_expected_mac.pdf.0.png
deleted file mode 100644
index e1ae9f2..0000000
--- a/testing/resources/pixel/bug_984811_expected_mac.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/bug_986108.in b/testing/resources/pixel/bug_986108.in
new file mode 100644
index 0000000..7af65f4
--- /dev/null
+++ b/testing/resources/pixel/bug_986108.in
@@ -0,0 +1,122 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 200 200]
+ /Contents 4 0 R
+ /Resources <<
+ /ProcSet [/PDF /Text]
+ /ExtGState <<
+ /GS0 5 0 R
+ >>
+ /XObject <<
+ /Fm0 6 0 R
+ >>
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+/GS0 gs
+/Fm0 Do
+Q
+endstream
+endobj
+{{object 5 0}} <<
+ /Type /ExtGState
+ /OP false
+ /op false
+ /AIS false
+ /BM /Normal
+ /OPM 0
+ /CA 1.0
+ /SA true
+ /ca 1.0
+ /SMask 7 0 R
+>>
+endobj
+{{object 6 0}} <<
+ /Subtype /Form
+ {{streamlen}}
+>>
+stream
+q
+0 0 50 60 re f
+Q
+endstream
+endobj
+{{object 7 0}} <<
+ /Type /Mask
+ /BC [0.0]
+ /S /Luminosity
+ /G 8 0 R
+>>
+endobj
+{{object 8 0}} <<
+ /Subtype /Form
+ /BBox [0 411 621 -10]
+ /Resources <<
+ /XObject <<
+ /Fm0 9 0 R
+ >>
+ >>
+{{streamlen}}
+>>
+stream
+q
+0 410 500 -440 re f
+/Fm0 Do
+Q
+endstream
+endobj
+{{object 9 0}} <<
+ /Subtype /Form
+ /Resources <<
+ /Shading <<
+ /Sh0 10 0 R
+ >>
+ >>
+ {{streamlen}}
+>>
+stream
+q
+/Sh0 sh
+Q
+endstream
+endobj
+{{object 10 0}} <<
+ /ShadingType 2
+ /ColorSpace /DeviceRGB
+ /Coords [0.0 0.0 1.0 0.0]
+ /BBox [-0.1 -0.9 1.1 0.87]
+ /Extend [true true]
+ /Function 11 0 R
+ /Domain [0.0 1.0]
+>>
+endobj
+{{object 11 0}} <<
+ /FunctionType 2
+ /Domain [0.0 1.0]
+ /C0 [0.8 0.1 0.2]
+ /C1 [0.2 0.025 0.05]
+ /N 1
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/bug_986108_expected.pdf.0.png b/testing/resources/pixel/bug_986108_expected.pdf.0.png
new file mode 100644
index 0000000..f97e340
--- /dev/null
+++ b/testing/resources/pixel/bug_986108_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/checkbox_radiobutton.evt b/testing/resources/pixel/checkbox_radiobutton.evt
new file mode 100644
index 0000000..c519dcb
--- /dev/null
+++ b/testing/resources/pixel/checkbox_radiobutton.evt
@@ -0,0 +1,11 @@
+# Check the checkbox
+mousemove,145,220
+mousedown,left,145,220
+mouseup,left,145,220
+
+# Tab to radio button from checkbox
+keycode,9
+keycode,9
+
+# Press Enter to select radio button
+charcode,13
\ No newline at end of file
diff --git a/testing/resources/pixel/checkbox_radiobutton.fragment b/testing/resources/pixel/checkbox_radiobutton.fragment
new file mode 100644
index 0000000..013992e
--- /dev/null
+++ b/testing/resources/pixel/checkbox_radiobutton.fragment
@@ -0,0 +1,187 @@
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 300 300]
+ /Contents 4 0 R
+ /Annots [5 0 R 6 0 R 8 0 R 10 0 R]
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+q
+1 w
+1 0 1 RG
+1 .752941 .796078 rg
+n 135.5 250.5 19 19 re B*
+Q
+q
+2 w
+.1 .1 .1 RG
+.8 .843 1 rg
+n 136 211 18 18 re B*
+Q
+q
+2 w
+0 .501961 0 RG
+0 0 1 rg
+n
+104 110 m
+104 114.9706 99.97056 119 95 119 c
+90.02944 119 86 114.9706 86 110 c
+86 105.0294 90.02944 101 95 101 c
+99.97056 101 104 105.0294 104 110 c
+B*
+Q
+q
+2 w
+0 .501961 0 RG
+0 0 1 rg
+n
+104 60 m
+104 64.97056 99.97056 69 95 69 c
+90.02944 69 86 64.97056 86 60 c
+86 55.02944 90.02944 51 95 51 c
+99.97056 51 104 55.02944 104 60 c
+B*
+Q
+Q
+endstream
+endobj
+{{object 5 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Btn
+ /Ff 1
+ /Rect [135 250 155 270]
+ /T (readOnlyCheckbox)
+ /AP <<
+ /N <<
+ /Yes 11 0 R
+ >>
+ >>
+ /AS /Off
+>>
+endobj
+{{object 6 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Btn
+ /Ff 2
+ /Rect [135 210 155 230]
+ /T (checkbox)
+ /AP <<
+ /N <<
+ /Yes 11 0 R
+ >>
+ >>
+ /AS /Off
+>>
+endobj
+{{object 7 0}} <<
+ /FT /Btn
+ /Ff 49153
+ /T (readOnlyRadioButton)
+ /Kids [8 0 R]
+>>
+endobj
+{{object 8 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /T (Widget8)
+ /FT /Btn
+ /Parent 7 0 R
+ /Rect [85 100 105 120]
+ /AP <<
+ /N <<
+ /value1 12 0 R
+ >>
+ >>
+ /AS /Off
+>>
+endobj
+{{object 9 0}} <<
+ /FT /Btn
+ /Ff 49154
+ /T (radioButton)
+ /Kids [10 0 R]
+>>
+endobj
+{{object 10 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Btn
+ /Parent 9 0 R
+ /Rect [85 50 105 70]
+ /AP <<
+ /N <<
+ /value1 12 0 R
+ >>
+ >>
+ /AS /Off
+>>
+endobj
+{{object 11 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /BBox [0 0 20 20]
+ {{streamlen}}
+>>
+stream
+q
+.1 .1 .1 rg .1 .1 .1 RG
+17.2 15.95145 m
+11.20694 10 l
+17.2 4.027746 l
+15.97225 2.8 l
+10 8.793064 l
+4.027746 2.8 l
+2.8 4.027746 l
+8.813873 10 l
+2.8 15.97225 l
+4.027746 17.2 l
+10 11.20694 l
+15.97225 17.2 l
+17.2 15.95145 l
+h
+f
+Q
+endstream
+endobj
+{{object 12 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /BBox [0 0 20 20]
+ {{streamlen}}
+>>
+stream
+q
+1 .752941 .796078 rg 1 .752941 .796078 RG
+17.2 15.95145 m
+11.20694 10 l
+17.2 4.027746 l
+15.97225 2.8 l
+10 8.793064 l
+4.027746 2.8 l
+2.8 4.027746 l
+8.813873 10 l
+2.8 15.97225 l
+4.027746 17.2 l
+10 11.20694 l
+15.97225 17.2 l
+17.2 15.95145 l
+h
+f
+Q
+endstream
+endobj
diff --git a/testing/resources/pixel/checkbox_radiobutton.in b/testing/resources/pixel/checkbox_radiobutton.in
new file mode 100644
index 0000000..116c638
--- /dev/null
+++ b/testing/resources/pixel/checkbox_radiobutton.in
@@ -0,0 +1,14 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /AcroForm <<
+ /Fields [5 0 R 6 0 R 7 0 R 9 0 R]
+ >>
+>>
+endobj
+{{include checkbox_radiobutton.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/checkbox_radiobutton_expected.pdf.0.png b/testing/resources/pixel/checkbox_radiobutton_expected.pdf.0.png
new file mode 100644
index 0000000..8baabb7
--- /dev/null
+++ b/testing/resources/pixel/checkbox_radiobutton_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/checkbox_radiobutton_expected_skia.pdf.0.png b/testing/resources/pixel/checkbox_radiobutton_expected_skia.pdf.0.png
new file mode 100644
index 0000000..fe713a6
--- /dev/null
+++ b/testing/resources/pixel/checkbox_radiobutton_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/checkbox_radiobutton_hide.in b/testing/resources/pixel/checkbox_radiobutton_hide.in
new file mode 100644
index 0000000..340b6d5
--- /dev/null
+++ b/testing/resources/pixel/checkbox_radiobutton_hide.in
@@ -0,0 +1,23 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /AcroForm <<
+ /Fields [5 0 R 6 0 R 7 0 R 9 0 R]
+ >>
+ /OpenAction 13 0 R
+>>
+endobj
+{{include checkbox_radiobutton.fragment}}
+% Make some buttons be hidden. Note that /T as an array of dict references
+% does not seem to work as expected.
+{{object 13 0}} <<
+ /Type /Action
+ /S /Hide
+ /T [(readOnlyRadioButton.Widget8) (readOnlyCheckbox)]
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/checkbox_radiobutton_hide_expected.pdf.0.png b/testing/resources/pixel/checkbox_radiobutton_hide_expected.pdf.0.png
new file mode 100644
index 0000000..361c05f
--- /dev/null
+++ b/testing/resources/pixel/checkbox_radiobutton_hide_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/checkbox_radiobutton_hide_expected_skia.pdf.0.png b/testing/resources/pixel/checkbox_radiobutton_hide_expected_skia.pdf.0.png
new file mode 100644
index 0000000..bdb2d6e
--- /dev/null
+++ b/testing/resources/pixel/checkbox_radiobutton_hide_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/checkbox_radiobutton_reset.in b/testing/resources/pixel/checkbox_radiobutton_reset.in
new file mode 100644
index 0000000..869107a
--- /dev/null
+++ b/testing/resources/pixel/checkbox_radiobutton_reset.in
@@ -0,0 +1,25 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /AcroForm <<
+ /Fields [5 0 R 6 0 R 7 0 R 9 0 R]
+ >>
+ /OpenAction 13 0 R
+>>
+endobj
+{{include checkbox_radiobutton.fragment}}
+% Make some buttons be reset. Note that /T as an array of dict references
+% does not seem to work as expected. /Flags 1 means all fields *except*
+% those listed.
+{{object 13 0}} <<
+ /Type /Action
+ /S /ResetForm
+ /Fields [(readOnlyRadioButton.Widget8) (readOnlyCheckbox)]
+ /Flags 1
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/checkbox_radiobutton_reset_expected.pdf.0.png b/testing/resources/pixel/checkbox_radiobutton_reset_expected.pdf.0.png
new file mode 100644
index 0000000..dd94188
--- /dev/null
+++ b/testing/resources/pixel/checkbox_radiobutton_reset_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/checkbox_radiobutton_reset_expected_skia.pdf.0.png b/testing/resources/pixel/checkbox_radiobutton_reset_expected_skia.pdf.0.png
new file mode 100644
index 0000000..057ecda
--- /dev/null
+++ b/testing/resources/pixel/checkbox_radiobutton_reset_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/combobox_form.evt b/testing/resources/pixel/combobox_form.evt
new file mode 100644
index 0000000..8211937
--- /dev/null
+++ b/testing/resources/pixel/combobox_form.evt
@@ -0,0 +1,10 @@
+# Check the combobox control
+mousemove,102,410
+mousedown,left,102,410
+mouseup,left,102,410
+
+# Press Enter to open the pop-up menu
+charcode,13
+
+# Press Space to make sure that the pop-up is not dismissed
+charcode,32
\ No newline at end of file
diff --git a/testing/resources/pixel/combobox_form.in b/testing/resources/pixel/combobox_form.in
new file mode 100644
index 0000000..56adea4
--- /dev/null
+++ b/testing/resources/pixel/combobox_form.in
@@ -0,0 +1,35 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /AcroForm <<
+ /Fields [4 0 R]
+ >>
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Annots [4 0 R]
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Ch
+ /Ff 131072
+ /T (Combo)
+ /Rect [100 400 200 430]
+ /Opt [() () ()]
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/combobox_form_expected.pdf.0.png b/testing/resources/pixel/combobox_form_expected.pdf.0.png
new file mode 100644
index 0000000..7ecd75d
--- /dev/null
+++ b/testing/resources/pixel/combobox_form_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/combobox_form_expected_skia.pdf.0.png b/testing/resources/pixel/combobox_form_expected_skia.pdf.0.png
new file mode 100644
index 0000000..4686e96
--- /dev/null
+++ b/testing/resources/pixel/combobox_form_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/font_size_expected.pdf.0.png b/testing/resources/pixel/font_size_expected.pdf.0.png
index b4506e0..91828b9 100644
--- a/testing/resources/pixel/font_size_expected.pdf.0.png
+++ b/testing/resources/pixel/font_size_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/font_size_expected_agg_mac.pdf.0.png b/testing/resources/pixel/font_size_expected_agg_mac.pdf.0.png
new file mode 100644
index 0000000..02e91d4
--- /dev/null
+++ b/testing/resources/pixel/font_size_expected_agg_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/font_size_expected_mac.pdf.0.png b/testing/resources/pixel/font_size_expected_mac.pdf.0.png
deleted file mode 100644
index dfd0690..0000000
--- a/testing/resources/pixel/font_size_expected_mac.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/font_size_expected_win.pdf.0.png b/testing/resources/pixel/font_size_expected_win.pdf.0.png
deleted file mode 100644
index a0d56aa..0000000
--- a/testing/resources/pixel/font_size_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/generation_numbers1_expected.pdf.0.png b/testing/resources/pixel/generation_numbers1_expected.pdf.0.png
index f2d3ba9..522a738 100644
--- a/testing/resources/pixel/generation_numbers1_expected.pdf.0.png
+++ b/testing/resources/pixel/generation_numbers1_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/generation_numbers1_expected_agg_mac.pdf.0.png b/testing/resources/pixel/generation_numbers1_expected_agg_mac.pdf.0.png
new file mode 100644
index 0000000..c946aa7
--- /dev/null
+++ b/testing/resources/pixel/generation_numbers1_expected_agg_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/generation_numbers1_expected_mac.pdf.0.png b/testing/resources/pixel/generation_numbers1_expected_mac.pdf.0.png
deleted file mode 100644
index 8d973ac..0000000
--- a/testing/resources/pixel/generation_numbers1_expected_mac.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/generation_numbers1_expected_win.pdf.0.png b/testing/resources/pixel/generation_numbers1_expected_win.pdf.0.png
deleted file mode 100644
index 7f1047c..0000000
--- a/testing/resources/pixel/generation_numbers1_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/generation_numbers2_expected.pdf.0.png b/testing/resources/pixel/generation_numbers2_expected.pdf.0.png
index f2d3ba9..522a738 100644
--- a/testing/resources/pixel/generation_numbers2_expected.pdf.0.png
+++ b/testing/resources/pixel/generation_numbers2_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/generation_numbers2_expected_agg_mac.pdf.0.png b/testing/resources/pixel/generation_numbers2_expected_agg_mac.pdf.0.png
new file mode 100644
index 0000000..c946aa7
--- /dev/null
+++ b/testing/resources/pixel/generation_numbers2_expected_agg_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/generation_numbers2_expected_mac.pdf.0.png b/testing/resources/pixel/generation_numbers2_expected_mac.pdf.0.png
deleted file mode 100644
index 8d973ac..0000000
--- a/testing/resources/pixel/generation_numbers2_expected_mac.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/generation_numbers2_expected_win.pdf.0.png b/testing/resources/pixel/generation_numbers2_expected_win.pdf.0.png
deleted file mode 100644
index 7f1047c..0000000
--- a/testing/resources/pixel/generation_numbers2_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/image_transformer_other.in b/testing/resources/pixel/image_transformer_other.in
new file mode 100644
index 0000000..5d1f580
--- /dev/null
+++ b/testing/resources/pixel/image_transformer_other.in
@@ -0,0 +1,128 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /MediaBox [0 0 200 200]
+ /Resources <<
+ /ColorSpace <<
+ /CS0 5 0 R
+ >>
+ /XObject <<
+ /ImMonoPalette 6 0 R
+ /ImMono 7 0 R
+ /ImColor 8 0 R
+ /ImColorWithMask 9 0 R
+ >>
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+-16 64 64 0 20 20 cm
+/ImMonoPalette Do
+Q
+q
+16 64 64 0 120 20 cm
+/ImMono Do
+Q
+q
+16 64 64 16 20 120 cm
+/ImColor Do
+Q
+q
+16 64 -64 16 120 120 cm
+/ImColorWithMask Do
+Q
+endstream
+endobj
+{{object 5 0}} [
+ /Indexed
+ /DeviceGray
+ 0
+ <cc>
+]
+endobj
+{{object 6 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 1
+ /ColorSpace 5 0 R
+ /Height 4
+ /Width 4
+ {{streamlen}}
+>>
+stream
+0000
+endstream
+endobj
+{{object 7 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /Width 4
+ /Height 4
+ /BitsPerComponent 8
+ /ColorSpace /DeviceGray
+ /Filter /ASCIIHexDecode
+ {{streamlen}}
+>>
+stream
+00000000
+33333333
+99999999
+cccccccc
+endstream
+endobj
+{{object 8 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 8
+ /ColorSpace /DeviceRGB
+ /Filter /ASCIIHexDecode
+ /Height 4
+ /Width 4
+ {{streamlen}}
+>>
+stream
+ff0000ff0000ff0000ff0000
+ff0000ff0000ff0000ff0000
+00ff0000ff0000ff0000ff00
+00ff0000ff0000ff0000ff00
+endstream
+endobj
+{{object 9 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 8
+ /ColorSpace /DeviceRGB
+ /Filter /ASCIIHexDecode
+ /Height 4
+ /Width 4
+ /Mask [0 255 0 0 0 0]
+ {{streamlen}}
+>>
+stream
+ff0000ff0000ff0000ff0000
+ff0000ff0000ff0000ff0000
+00ff0000ff0000ff0000ff00
+00ff0000ff0000ff0000ff00
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/image_transformer_other_expected.pdf.0.png b/testing/resources/pixel/image_transformer_other_expected.pdf.0.png
new file mode 100644
index 0000000..e9aab95
--- /dev/null
+++ b/testing/resources/pixel/image_transformer_other_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/image_transformer_other_expected_skia.pdf.0.png b/testing/resources/pixel/image_transformer_other_expected_skia.pdf.0.png
new file mode 100644
index 0000000..e65f919
--- /dev/null
+++ b/testing/resources/pixel/image_transformer_other_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/jpxdecode.in b/testing/resources/pixel/jpxdecode.in
new file mode 100644
index 0000000..d69b058
--- /dev/null
+++ b/testing/resources/pixel/jpxdecode.in
@@ -0,0 +1,177 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /MediaBox [0 0 88 128]
+ /Resources <<
+ /XObject <<
+ /ImGray 5 0 R
+ /ImGrayAlpha 6 0 R
+ /ImRGB 7 0 R
+ /ImRGBAlpha 8 0 R
+ /ImCMYK 9 0 R
+ /ImCMYKAlpha 10 0 R
+ >>
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+
+% 50% gray background rectangle
+q
+ 0.5 0.5 0.5 rg
+ 0 0 88 128 re
+ f
+Q
+
+% grayscale, grayscale with alpha
+q
+ 32 0 0 32 8 88 cm
+ /ImGray Do
+Q
+q
+ 32 0 0 32 48 88 cm
+ /ImGrayAlpha Do
+Q
+
+% RGB, RGB with alpha
+q
+ 32 0 0 32 8 48 cm
+ /ImRGB Do
+Q
+q
+ 32 0 0 32 48 48 cm
+ /ImRGBAlpha Do
+Q
+
+% CMYK, CMYK with alpha
+q
+ 32 0 0 32 8 8 cm
+ /ImCMYK Do
+Q
+q
+ 32 0 0 32 48 8 cm
+ /ImCMYKAlpha Do
+Q
+
+endstream
+endobj
+
+% grayscale
+{{object 5 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 8
+ /ColorSpace /DeviceGray
+ /Filter /JPXDecode
+ /Height 4
+ /Width 4
+ {{streamlen}}
+>>
+stream
+{{include ../gray.jp2}}
+endstream
+endobj
+
+% grayscale with opacity
+{{object 6 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 8
+ /ColorSpace /DeviceGray
+ /Filter /JPXDecode
+ /Height 4
+ /SMaskInData 1
+ /Width 4
+ {{streamlen}}
+>>
+stream
+{{include ../gray-alpha.jp2}}
+endstream
+endobj
+
+% RGB
+{{object 7 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 8
+ /ColorSpace /DeviceRGB
+ /Filter /JPXDecode
+ /Height 4
+ /Width 4
+ {{streamlen}}
+>>
+stream
+{{include ../RGB.jp2}}
+endstream
+endobj
+
+% RGB with opacity
+{{object 8 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 8
+ /ColorSpace /DeviceRGB
+ /Filter /JPXDecode
+ /Height 4
+ /SMaskInData 1
+ /Width 4
+ {{streamlen}}
+>>
+stream
+{{include ../RGB-alpha.jp2}}
+endstream
+endobj
+
+% CMYK
+{{object 9 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 8
+ /ColorSpace /DeviceCMYK
+ /Filter /JPXDecode
+ /Height 4
+ /Width 4
+ {{streamlen}}
+>>
+stream
+{{include ../CMYK.jpf}}
+endstream
+endobj
+
+% CMYK with opacity.
+{{object 10 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 8
+ /ColorSpace /DeviceCMYK
+ /Filter /JPXDecode
+ /Height 4
+ /SMaskInData 1
+ /Width 4
+ {{streamlen}}
+>>
+stream
+{{include ../CMYK-alpha.jpf}}
+endstream
+endobj
+
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/jpxdecode_expected.pdf.0.png b/testing/resources/pixel/jpxdecode_expected.pdf.0.png
new file mode 100644
index 0000000..9051cb0
--- /dev/null
+++ b/testing/resources/pixel/jpxdecode_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/jpxdecode_with_mismatch_colorspace.in b/testing/resources/pixel/jpxdecode_with_mismatch_colorspace.in
new file mode 100644
index 0000000..ddf3068
--- /dev/null
+++ b/testing/resources/pixel/jpxdecode_with_mismatch_colorspace.in
@@ -0,0 +1,180 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /MediaBox [0 0 88 128]
+ /Resources <<
+ /XObject <<
+ /ImGrayCsRGB 5 0 R
+ /ImGrayCsCMYK 6 0 R
+ /ImRGBCsGray 7 0 R
+ /ImRGBCsCMYK 8 0 R
+ /ImCMYKCsGray 9 0 R
+ /ImCMYKCsRGB 10 0 R
+ >>
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+
+% 50% gray background rectangle
+q
+ 0.5 0.5 0.5 rg
+ 0 0 88 128 re
+ f
+Q
+
+% grayscale image with RGB /ColorSpace entry
+q
+ 32 0 0 32 8 88 cm
+ /ImGrayCsRGB Do
+Q
+
+% grayscale image with CMYK /ColorSpace entry
+q
+ 32 0 0 32 48 88 cm
+ /ImGrayCsCMYK Do
+Q
+
+% RGB image with grayscale /ColorSpace entry
+q
+ 32 0 0 32 8 48 cm
+ /ImRGBCsGray Do
+Q
+
+% RGB image with CMYK /ColorSpace entry
+q
+ 32 0 0 32 48 48 cm
+ /ImRGBCsCMYK Do
+Q
+
+% CMYK image with grayscale /ColorSpace entry
+q
+ 32 0 0 32 8 8 cm
+ /ImCMYKCsGray Do
+Q
+
+% CMYK image with RGB /ColorSpace entry
+q
+ 32 0 0 32 48 8 cm
+ /ImCMYKCsRGB Do
+Q
+
+endstream
+endobj
+
+% grayscale image with RGB /ColorSpace
+{{object 5 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 8
+ /ColorSpace /DeviceRGB
+ /Filter /JPXDecode
+ /Height 4
+ /Width 4
+ {{streamlen}}
+>>
+stream
+{{include ../gray.jp2}}
+endstream
+endobj
+
+% grayscale image with CMYK /ColorSpace
+{{object 6 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 8
+ /ColorSpace /DeviceCMYK
+ /Filter /JPXDecode
+ /Height 4
+ /Width 4
+ {{streamlen}}
+>>
+stream
+{{include ../gray.jp2}}
+endstream
+endobj
+
+% RGB image with grayscale /ColorSpace
+{{object 7 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 8
+ /ColorSpace /DeviceGray
+ /Filter /JPXDecode
+ /Height 4
+ /Width 4
+ {{streamlen}}
+>>
+stream
+{{include ../RGB.jp2}}
+endstream
+endobj
+
+% RGB image with CMYK /ColorSpace
+{{object 8 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 8
+ /ColorSpace /DeviceCMYK
+ /Filter /JPXDecode
+ /Height 4
+ /Width 4
+ {{streamlen}}
+>>
+stream
+{{include ../RGB.jp2}}
+endstream
+endobj
+
+% CMYK image with grayscale /ColorSpace
+{{object 9 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 8
+ /ColorSpace /DeviceGray
+ /Filter /JPXDecode
+ /Height 4
+ /Width 4
+ {{streamlen}}
+>>
+stream
+{{include ../CMYK.jpf}}
+endstream
+endobj
+
+% CMYK image with RGB /ColorSpace
+{{object 10 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 8
+ /ColorSpace /DeviceRGB
+ /Filter /JPXDecode
+ /Height 4
+ /Width 4
+ {{streamlen}}
+>>
+stream
+{{include ../CMYK.jpf}}
+endstream
+endobj
+
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/jpxdecode_with_mismatch_colorspace_expected.pdf.0.png b/testing/resources/pixel/jpxdecode_with_mismatch_colorspace_expected.pdf.0.png
new file mode 100644
index 0000000..65743e0
--- /dev/null
+++ b/testing/resources/pixel/jpxdecode_with_mismatch_colorspace_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/jpxdecode_without_bitspercomponent.in b/testing/resources/pixel/jpxdecode_without_bitspercomponent.in
new file mode 100644
index 0000000..59b516f
--- /dev/null
+++ b/testing/resources/pixel/jpxdecode_without_bitspercomponent.in
@@ -0,0 +1,171 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /MediaBox [0 0 88 128]
+ /Resources <<
+ /XObject <<
+ /ImGray 5 0 R
+ /ImGrayAlpha 6 0 R
+ /ImRGB 7 0 R
+ /ImRGBAlpha 8 0 R
+ /ImCMYK 9 0 R
+ /ImCMYKAlpha 10 0 R
+ >>
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+
+% 50% gray background rectangle
+q
+ 0.5 0.5 0.5 rg
+ 0 0 88 128 re
+ f
+Q
+
+% grayscale, grayscale with alpha
+q
+ 32 0 0 32 8 88 cm
+ /ImGray Do
+Q
+q
+ 32 0 0 32 48 88 cm
+ /ImGrayAlpha Do
+Q
+
+% RGB, RGB with alpha
+q
+ 32 0 0 32 8 48 cm
+ /ImRGB Do
+Q
+q
+ 32 0 0 32 48 48 cm
+ /ImRGBAlpha Do
+Q
+
+% CMYK, CMYK with alpha
+q
+ 32 0 0 32 8 8 cm
+ /ImCMYK Do
+Q
+q
+ 32 0 0 32 48 8 cm
+ /ImCMYKAlpha Do
+Q
+
+endstream
+endobj
+
+% grayscale
+{{object 5 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /ColorSpace /DeviceGray
+ /Filter /JPXDecode
+ /Height 4
+ /Width 4
+ {{streamlen}}
+>>
+stream
+{{include ../gray.jp2}}
+endstream
+endobj
+
+% grayscale with opacity
+{{object 6 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /ColorSpace /DeviceGray
+ /Filter /JPXDecode
+ /Height 4
+ /SMaskInData 1
+ /Width 4
+ {{streamlen}}
+>>
+stream
+{{include ../gray-alpha.jp2}}
+endstream
+endobj
+
+% RGB
+{{object 7 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /ColorSpace /DeviceRGB
+ /Filter /JPXDecode
+ /Height 4
+ /Width 4
+ {{streamlen}}
+>>
+stream
+{{include ../RGB.jp2}}
+endstream
+endobj
+
+% RGB with opacity
+{{object 8 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /ColorSpace /DeviceRGB
+ /Filter /JPXDecode
+ /Height 4
+ /SMaskInData 1
+ /Width 4
+ {{streamlen}}
+>>
+stream
+{{include ../RGB-alpha.jp2}}
+endstream
+endobj
+
+% CMYK
+{{object 9 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /ColorSpace /DeviceCMYK
+ /Filter /JPXDecode
+ /Height 4
+ /Width 4
+ {{streamlen}}
+>>
+stream
+{{include ../CMYK.jpf}}
+endstream
+endobj
+
+% CMYK with opacity.
+{{object 10 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /ColorSpace /DeviceCMYK
+ /Filter /JPXDecode
+ /Height 4
+ /SMaskInData 1
+ /Width 4
+ {{streamlen}}
+>>
+stream
+{{include ../CMYK-alpha.jpf}}
+endstream
+endobj
+
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/jpxdecode_without_bitspercomponent_expected.pdf.0.png b/testing/resources/pixel/jpxdecode_without_bitspercomponent_expected.pdf.0.png
new file mode 100644
index 0000000..9051cb0
--- /dev/null
+++ b/testing/resources/pixel/jpxdecode_without_bitspercomponent_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/jpxdecode_without_colorspace.in b/testing/resources/pixel/jpxdecode_without_colorspace.in
new file mode 100644
index 0000000..0ffb64f
--- /dev/null
+++ b/testing/resources/pixel/jpxdecode_without_colorspace.in
@@ -0,0 +1,171 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /MediaBox [0 0 88 128]
+ /Resources <<
+ /XObject <<
+ /ImGray 5 0 R
+ /ImGrayAlpha 6 0 R
+ /ImRGB 7 0 R
+ /ImRGBAlpha 8 0 R
+ /ImCMYK 9 0 R
+ /ImCMYKAlpha 10 0 R
+ >>
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+
+% 50% gray background rectangle
+q
+ 0.5 0.5 0.5 rg
+ 0 0 88 128 re
+ f
+Q
+
+% grayscale, grayscale with alpha
+q
+ 32 0 0 32 8 88 cm
+ /ImGray Do
+Q
+q
+ 32 0 0 32 48 88 cm
+ /ImGrayAlpha Do
+Q
+
+% RGB, RGB with alpha
+q
+ 32 0 0 32 8 48 cm
+ /ImRGB Do
+Q
+q
+ 32 0 0 32 48 48 cm
+ /ImRGBAlpha Do
+Q
+
+% CMYK, CMYK with alpha
+q
+ 32 0 0 32 8 8 cm
+ /ImCMYK Do
+Q
+q
+ 32 0 0 32 48 8 cm
+ /ImCMYKAlpha Do
+Q
+
+endstream
+endobj
+
+% grayscale
+{{object 5 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 8
+ /Filter /JPXDecode
+ /Height 4
+ /Width 4
+ {{streamlen}}
+>>
+stream
+{{include ../gray.jp2}}
+endstream
+endobj
+
+% grayscale with opacity
+{{object 6 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 8
+ /Filter /JPXDecode
+ /Height 4
+ /SMaskInData 1
+ /Width 4
+ {{streamlen}}
+>>
+stream
+{{include ../gray-alpha.jp2}}
+endstream
+endobj
+
+% RGB
+{{object 7 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 8
+ /Filter /JPXDecode
+ /Height 4
+ /Width 4
+ {{streamlen}}
+>>
+stream
+{{include ../RGB.jp2}}
+endstream
+endobj
+
+% RGB with opacity
+{{object 8 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 8
+ /Filter /JPXDecode
+ /Height 4
+ /SMaskInData 1
+ /Width 4
+ {{streamlen}}
+>>
+stream
+{{include ../RGB-alpha.jp2}}
+endstream
+endobj
+
+% CMYK
+{{object 9 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 8
+ /Filter /JPXDecode
+ /Height 4
+ /Width 4
+ {{streamlen}}
+>>
+stream
+{{include ../CMYK.jpf}}
+endstream
+endobj
+
+% CMYK with opacity.
+{{object 10 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 8
+ /Filter /JPXDecode
+ /Height 4
+ /SMaskInData 1
+ /Width 4
+ {{streamlen}}
+>>
+stream
+{{include ../CMYK-alpha.jpf}}
+endstream
+endobj
+
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/jpxdecode_without_colorspace_expected.pdf.0.png b/testing/resources/pixel/jpxdecode_without_colorspace_expected.pdf.0.png
new file mode 100644
index 0000000..9051cb0
--- /dev/null
+++ b/testing/resources/pixel/jpxdecode_without_colorspace_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/jpxdecode_without_smaskindata.in b/testing/resources/pixel/jpxdecode_without_smaskindata.in
new file mode 100644
index 0000000..ab29a60
--- /dev/null
+++ b/testing/resources/pixel/jpxdecode_without_smaskindata.in
@@ -0,0 +1,111 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /MediaBox [0 0 48 128]
+ /Resources <<
+ /XObject <<
+ /ImGrayAlpha 5 0 R
+ /ImRGBAlpha 6 0 R
+ /ImCMYKAlpha 7 0 R
+ >>
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+
+% 50% gray background rectangle
+q
+ 0.5 0.5 0.5 rg
+ 0 0 48 128 re
+ f
+Q
+
+% grayscale with ignored alpha
+q
+ 32 0 0 32 8 88 cm
+ /ImGrayAlpha Do
+Q
+
+% RGB with ignored alpha
+q
+ 32 0 0 32 8 48 cm
+ /ImRGBAlpha Do
+Q
+
+% CMYK with ignored alpha
+q
+ 32 0 0 32 8 8 cm
+ /ImCMYKAlpha Do
+Q
+
+endstream
+endobj
+
+% grayscale with opacity, but the soft-mask information is not used.
+{{object 5 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 8
+ /ColorSpace /DeviceGray
+ /Filter /JPXDecode
+ /Height 4
+ /Width 4
+ {{streamlen}}
+>>
+stream
+{{include ../gray-alpha.jp2}}
+endstream
+endobj
+
+% RGB with opacity, but the soft-mask information is not used.
+{{object 6 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 8
+ /ColorSpace /DeviceRGB
+ /Filter /JPXDecode
+ /Height 4
+ /Width 4
+ {{streamlen}}
+>>
+stream
+{{include ../RGB-alpha.jp2}}
+endstream
+endobj
+
+% CMYK with opacity, but the soft-mask information is not used.
+{{object 7 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /BitsPerComponent 8
+ /ColorSpace /DeviceCMYK
+ /Filter /JPXDecode
+ /Height 4
+ /Width 4
+ {{streamlen}}
+>>
+stream
+{{include ../CMYK-alpha.jpf}}
+endstream
+endobj
+
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/jpxdecode_without_smaskindata_expected.pdf.0.png b/testing/resources/pixel/jpxdecode_without_smaskindata_expected.pdf.0.png
new file mode 100644
index 0000000..5405d1e
--- /dev/null
+++ b/testing/resources/pixel/jpxdecode_without_smaskindata_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/long_dashed_line.in b/testing/resources/pixel/long_dashed_line.in
new file mode 100644
index 0000000..623adfa
--- /dev/null
+++ b/testing/resources/pixel/long_dashed_line.in
@@ -0,0 +1,33 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /MediaBox [0 0 800 800]
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+[2 2] 0 d
+100 100 m
+100 -75697008 l
+S
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/long_dashed_line_expected.pdf.0.png b/testing/resources/pixel/long_dashed_line_expected.pdf.0.png
new file mode 100644
index 0000000..41b3efb
--- /dev/null
+++ b/testing/resources/pixel/long_dashed_line_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/long_dashed_line_expected_skia.pdf.0.png b/testing/resources/pixel/long_dashed_line_expected_skia.pdf.0.png
new file mode 100644
index 0000000..3ffc2d6
--- /dev/null
+++ b/testing/resources/pixel/long_dashed_line_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/matte_expected_skia.pdf.0.png b/testing/resources/pixel/matte_expected_skia.pdf.0.png
new file mode 100644
index 0000000..bdab404
--- /dev/null
+++ b/testing/resources/pixel/matte_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/password.evt b/testing/resources/pixel/password.evt
new file mode 100644
index 0000000..3051015
--- /dev/null
+++ b/testing/resources/pixel/password.evt
@@ -0,0 +1,26 @@
+mousemove,102,102
+mousedown,left,102,102
+mouseup,left,102,102
+charcode,116
+charcode,105
+charcode,103
+charcode,101
+charcode,114
+charcode,115
+charcode,115
+charcode,115
+charcode,115
+
+mousemove,102,162
+mousedown,left,102,162
+mouseup,left,102,162
+charcode,116
+charcode,105
+charcode,103
+charcode,101
+charcode,114
+charcode,115
+charcode,115
+charcode,115
+charcode,115
+
diff --git a/testing/resources/pixel/password.in b/testing/resources/pixel/password.in
new file mode 100644
index 0000000..a50f0af
--- /dev/null
+++ b/testing/resources/pixel/password.in
@@ -0,0 +1,54 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /AcroForm <<
+ /Fields [
+ 4 0 R
+ 5 0 R
+ ]
+ /NeedAppearances true
+ >>
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [ 3 0 R ]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [ 0 0 300 200 ]
+ /Annots [
+ 4 0 R
+ 5 0 R
+ ]
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Tx
+ /T (Text Box)
+ /Rect [ 100 100 200 130 ]
+ /MaxLen 5
+ /Q 1
+>>
+endobj
+{{object 5 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Tx
+ /Ff 8192
+ /T (Password Box)
+ /Rect [ 100 160 200 190 ]
+ /MaxLen 5
+ /Q 2
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/password_expected.pdf.0.png b/testing/resources/pixel/password_expected.pdf.0.png
new file mode 100644
index 0000000..5040ef6
--- /dev/null
+++ b/testing/resources/pixel/password_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/password_expected_mac.pdf.0.png b/testing/resources/pixel/password_expected_mac.pdf.0.png
new file mode 100644
index 0000000..2f4239f
--- /dev/null
+++ b/testing/resources/pixel/password_expected_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/password_expected_skia.pdf.0.png b/testing/resources/pixel/password_expected_skia.pdf.0.png
new file mode 100644
index 0000000..01b4b68
--- /dev/null
+++ b/testing/resources/pixel/password_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/radial_shading_point_at_border_expected_skia.pdf.0.png b/testing/resources/pixel/radial_shading_point_at_border_expected_skia.pdf.0.png
new file mode 100644
index 0000000..92f2349
--- /dev/null
+++ b/testing/resources/pixel/radial_shading_point_at_border_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/radial_shading_point_at_border_no_extend_expected_skia.pdf.0.png b/testing/resources/pixel/radial_shading_point_at_border_no_extend_expected_skia.pdf.0.png
new file mode 100644
index 0000000..846a9ea
--- /dev/null
+++ b/testing/resources/pixel/radial_shading_point_at_border_no_extend_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/radial_shading_point_at_center_expected_skia.pdf.0.png b/testing/resources/pixel/radial_shading_point_at_center_expected_skia.pdf.0.png
new file mode 100644
index 0000000..756ddb8
--- /dev/null
+++ b/testing/resources/pixel/radial_shading_point_at_center_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/reset_button.evt b/testing/resources/pixel/reset_button.evt
new file mode 100644
index 0000000..1606926
--- /dev/null
+++ b/testing/resources/pixel/reset_button.evt
@@ -0,0 +1,10 @@
+# Check the checkbox
+mousemove,145,220
+mousedown,left,145,220
+mouseup,left,145,220
+
+# Tab to reset button from checkbox
+keycode,9
+
+# Press Enter to reset the form
+charcode,13
\ No newline at end of file
diff --git a/testing/resources/pixel/reset_button.in b/testing/resources/pixel/reset_button.in
new file mode 100644
index 0000000..6f93570
--- /dev/null
+++ b/testing/resources/pixel/reset_button.in
@@ -0,0 +1,97 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /AcroForm <<
+ /Fields [5 0 R 6 0 R]
+ >>
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 300 300]
+ /Contents 4 0 R
+ /Annots [5 0 R 6 0 R]
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+2 w
+.1 .1 .1 RG
+.8 .843 1 rg
+n 136 211 18 18 re B*
+Q
+endstream
+endobj
+{{object 5 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Btn
+ /F 4
+ /Ff 2
+ /P 3 0 R
+ /Rect [135 210 155 230]
+ /T (checkbox)
+ /AP <<
+ /N <<
+ /Yes 7 0 R
+ >>
+ >>
+ /AS /Off
+ /V /Off
+>>
+endobj
+{{object 6 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Btn
+ /F 4
+ /P 3 0 R
+ /Rect [135 110 185 150]
+ /T (Reset button)
+ /Ff 65536
+ /A <<
+ /S /ResetForm
+ /Fields [5 0 R]
+ /Flags 1
+ >>
+ /MK <<
+ /BC [0.0 0.0 0.4]
+ /BG [0.9 0.9 0.9]
+ >>
+>>
+endobj
+{{object 7 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /FormType 1
+ /BBox [0 0 20 20]
+ {{streamlen}}
+>>
+stream
+q
+17.2 15.95145 m
+17.2 4.027746 l
+15.97225 2.8 l
+4.027746 2.8 l
+4.027746 17.2 l
+15.97225 17.2 l
+h
+f
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
\ No newline at end of file
diff --git a/testing/resources/pixel/reset_button_expected.pdf.0.png b/testing/resources/pixel/reset_button_expected.pdf.0.png
new file mode 100644
index 0000000..e36c30d
--- /dev/null
+++ b/testing/resources/pixel/reset_button_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/scrollable_widgets1.evt b/testing/resources/pixel/scrollable_widgets1.evt
new file mode 100644
index 0000000..c595cd7
--- /dev/null
+++ b/testing/resources/pixel/scrollable_widgets1.evt
@@ -0,0 +1,29 @@
+# Must move the mouse and click to give widget focus.
+mousemove,150,415
+mousedown,left,150,415
+# Scroll all the way down.
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
diff --git a/testing/resources/pixel/scrollable_widgets1.in b/testing/resources/pixel/scrollable_widgets1.in
new file mode 100644
index 0000000..9453b7c
--- /dev/null
+++ b/testing/resources/pixel/scrollable_widgets1.in
@@ -0,0 +1,52 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /AcroForm <<
+ /Fields [5 0 R]
+ /DR <<
+ /Font <<
+ /F1 4 0 R
+ >>
+ >>
+ >>
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 300 600]
+ /Annots [5 0 R]
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Helvetica
+>>
+endobj
+{{object 5 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Ch
+ /Ff 2097152
+ /T (Listbox_MultiSelect)
+ /DA (0 0 0 rg /F1 12 Tf)
+ /Rect [100 400 200 430]
+ /Opt [(Apple) (Banana) (Cherry) (Date) (Elderberry) (Fig) (Guava) (Honeydew)
+ (Indian Fig) (Jackfruit) (Kiwi) (Lemon) (Mango) (Nectarine) (Orange)
+ (Persimmon) (Quince) (Raspberry) (Strawberry) (Tamarind) (Ugli Fruit)
+ (Voavanga) (Wolfberry) (Xigua) (Yangmei) (Zucchini)]
+ /V (Banana)
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/scrollable_widgets1_expected.pdf.0.png b/testing/resources/pixel/scrollable_widgets1_expected.pdf.0.png
new file mode 100644
index 0000000..7760af7
--- /dev/null
+++ b/testing/resources/pixel/scrollable_widgets1_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/scrollable_widgets1_expected_mac.pdf.0.png b/testing/resources/pixel/scrollable_widgets1_expected_mac.pdf.0.png
new file mode 100644
index 0000000..5dbdc5a
--- /dev/null
+++ b/testing/resources/pixel/scrollable_widgets1_expected_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/scrollable_widgets1_expected_skia.pdf.0.png b/testing/resources/pixel/scrollable_widgets1_expected_skia.pdf.0.png
new file mode 100644
index 0000000..d4c1f9d
--- /dev/null
+++ b/testing/resources/pixel/scrollable_widgets1_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/scrollable_widgets2.evt b/testing/resources/pixel/scrollable_widgets2.evt
new file mode 100644
index 0000000..59b8a4b
--- /dev/null
+++ b/testing/resources/pixel/scrollable_widgets2.evt
@@ -0,0 +1,34 @@
+# Must move the mouse and click to give widget focus.
+mousemove,150,415
+mousedown,left,150,415
+# Scroll all the way down and then scroll back up a bit.
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,-1
+mousewheel,150,415,0,1
+mousewheel,150,415,0,1
+mousewheel,150,415,0,1
+mousewheel,150,415,0,1
+mousewheel,150,415,0,1
diff --git a/testing/resources/pixel/scrollable_widgets2.in b/testing/resources/pixel/scrollable_widgets2.in
new file mode 100644
index 0000000..9453b7c
--- /dev/null
+++ b/testing/resources/pixel/scrollable_widgets2.in
@@ -0,0 +1,52 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /AcroForm <<
+ /Fields [5 0 R]
+ /DR <<
+ /Font <<
+ /F1 4 0 R
+ >>
+ >>
+ >>
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 300 600]
+ /Annots [5 0 R]
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Helvetica
+>>
+endobj
+{{object 5 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Ch
+ /Ff 2097152
+ /T (Listbox_MultiSelect)
+ /DA (0 0 0 rg /F1 12 Tf)
+ /Rect [100 400 200 430]
+ /Opt [(Apple) (Banana) (Cherry) (Date) (Elderberry) (Fig) (Guava) (Honeydew)
+ (Indian Fig) (Jackfruit) (Kiwi) (Lemon) (Mango) (Nectarine) (Orange)
+ (Persimmon) (Quince) (Raspberry) (Strawberry) (Tamarind) (Ugli Fruit)
+ (Voavanga) (Wolfberry) (Xigua) (Yangmei) (Zucchini)]
+ /V (Banana)
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/scrollable_widgets2_expected.pdf.0.png b/testing/resources/pixel/scrollable_widgets2_expected.pdf.0.png
new file mode 100644
index 0000000..06e06ec
--- /dev/null
+++ b/testing/resources/pixel/scrollable_widgets2_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/scrollable_widgets2_expected_mac.pdf.0.png b/testing/resources/pixel/scrollable_widgets2_expected_mac.pdf.0.png
new file mode 100644
index 0000000..aa4917f
--- /dev/null
+++ b/testing/resources/pixel/scrollable_widgets2_expected_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/scrollable_widgets2_expected_skia.pdf.0.png b/testing/resources/pixel/scrollable_widgets2_expected_skia.pdf.0.png
new file mode 100644
index 0000000..dd9f6a1
--- /dev/null
+++ b/testing/resources/pixel/scrollable_widgets2_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/text_form_custom_font.in b/testing/resources/pixel/text_form_custom_font.in
new file mode 100644
index 0000000..af404e4
--- /dev/null
+++ b/testing/resources/pixel/text_form_custom_font.in
@@ -0,0 +1,81 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /AcroForm <<
+ /DR <<
+ /Font <<
+ /TT0 5 0 R
+ >>
+ >>
+ /Fields [4 0 R]
+ >>
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Annots [4 0 R]
+ /MediaBox [0 0 200 200]
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Tx
+ /DA (1 0 0 rg /TT0 16 Tf)
+ /Rect [50 100 150 130]
+ /T (Text Box)
+ /V ( )
+>>
+endobj
+{{object 5 0}} <<
+ /Type /Font
+ /Subtype /TrueType
+ /FirstChar 32
+ /BaseFont /AAAAAD+Test
+ /FontDescriptor 6 0 R
+ /ToUnicode 7 0 R
+ /LastChar 32
+ /Widths [1055]
+>>
+endobj
+{{object 6 0}} <<
+ /Type /FontDescriptor
+ /Descent -68
+ /MissingWidth 1000
+ /CapHeight 1149
+ /StemV 0
+ /FontFile2 8 0 R
+ /Flags 4
+ /FontBBox [0 -215 1000 932]
+ /FontName /AAAAAD+Test
+ /ItalicAngle 0
+ /Ascent 933
+>>
+endobj
+{{object 7 0}} <<
+ {{streamlen}}
+>>
+stream
+{{include ../bug_1388_cmap.fragment}}
+endstream
+endobj
+{{object 8 0}} <<
+ /Filter [/ASCIIHexDecode /FlateDecode]
+ {{streamlen}}
+>>
+stream
+{{include ../bug_1388_truetype_font.fragment}}
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/text_form_custom_font_expected.pdf.0.png b/testing/resources/pixel/text_form_custom_font_expected.pdf.0.png
new file mode 100644
index 0000000..3894ad5
--- /dev/null
+++ b/testing/resources/pixel/text_form_custom_font_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/text_form_custom_font_expected_mac.pdf.0.png b/testing/resources/pixel/text_form_custom_font_expected_mac.pdf.0.png
new file mode 100644
index 0000000..01b3dbf
--- /dev/null
+++ b/testing/resources/pixel/text_form_custom_font_expected_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/text_form_custom_font_expected_skia.pdf.0.png b/testing/resources/pixel/text_form_custom_font_expected_skia.pdf.0.png
new file mode 100644
index 0000000..47582b6
--- /dev/null
+++ b/testing/resources/pixel/text_form_custom_font_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/type3_expected.pdf.0.png b/testing/resources/pixel/type3_expected.pdf.0.png
index 6837bb8..63d9e57 100644
--- a/testing/resources/pixel/type3_expected.pdf.0.png
+++ b/testing/resources/pixel/type3_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/use_symbolneu/bug_1449.in b/testing/resources/pixel/use_symbolneu/bug_1449.in
new file mode 100644
index 0000000..603eb56
--- /dev/null
+++ b/testing/resources/pixel/use_symbolneu/bug_1449.in
@@ -0,0 +1,47 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /MediaBox [ 0 0 100 100 ]
+ /Count 1
+ /Kids [ 3 0 R ]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Resources <<
+ /Font <<
+ /F0 4 0 R
+ >>
+ >>
+ /Contents 5 0 R
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Font
+ /Subtype /TrueType
+ /BaseFont /Symbol
+ /FirstChar 31
+ /LastChar 255
+ /Name /F0
+>>
+endobj
+{{object 5 0}} <<
+ {{streamlen}}
+>>
+stream
+BT
+25 25 Td /F0 12 Tf
+-0.036 Tc (\265) Tj
+ET
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/use_symbolneu/bug_1449_expected.pdf.0.png b/testing/resources/pixel/use_symbolneu/bug_1449_expected.pdf.0.png
new file mode 100644
index 0000000..319932a
--- /dev/null
+++ b/testing/resources/pixel/use_symbolneu/bug_1449_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/barcode_test_expected_skia.pdf.0.png b/testing/resources/pixel/xfa_specific/barcode_test_expected_skia.pdf.0.png
new file mode 100644
index 0000000..3716b9c
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/barcode_test_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/barcode_test_expected_win.pdf.0.png b/testing/resources/pixel/xfa_specific/barcode_test_expected_win.pdf.0.png
deleted file mode 100644
index 23b80a7..0000000
--- a/testing/resources/pixel/xfa_specific/barcode_test_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/bug_1282_expected_skia.pdf.0.png b/testing/resources/pixel/xfa_specific/bug_1282_expected_skia.pdf.0.png
new file mode 100644
index 0000000..a29272f
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/bug_1282_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/bug_1286970.in b/testing/resources/pixel/xfa_specific/bug_1286970.in
new file mode 100644
index 0000000..4454de7
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/bug_1286970.in
@@ -0,0 +1,31 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+ {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/3.3/">
+ <subform layout="rl-tb" name="subform1">
+ <pageSet>
+ <pageArea name="Page1" id="Page1">
+ <contentArea x="18pt" y="18pt" w="72pt" h="72pt"/>
+ <medium stock="default" short="72pt" long="72pt"/>
+ </pageArea>
+ </pageSet>
+ <subformSet>
+ <subform><field h="73pt"></field></subform>
+ </subformSet>
+ </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/xfa_specific/bug_1286970_expected.pdf.0.png b/testing/resources/pixel/xfa_specific/bug_1286970_expected.pdf.0.png
new file mode 100644
index 0000000..9b4c2d8
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/bug_1286970_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/bug_1337.in b/testing/resources/pixel/xfa_specific/bug_1337.in
new file mode 100644
index 0000000..d44a496
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/bug_1337.in
@@ -0,0 +1,38 @@
+{{header}}
+{{include ../../xfa_catalog_1_0.fragment}}
+{{include ../../xfa_object_2_0.fragment}}
+{{include ../../xfa_preamble_3_0.fragment}}
+{{include ../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+ {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/3.3/">
+ <subform name="form1" layout="tb" locale="en_US" restoreState="auto">
+ <pageSet>
+ <pageArea name="Page1" id="Page1">
+ <contentArea x="18pt" y="18pt" w="612pt" h="792pt"/>
+ <medium stock="default" short="612pt" long="792pt"/>
+ </pageArea>
+ </pageSet>
+ <subform w="576pt" h="756pt" name="Page1">
+ <draw name="StaticImage1" w="250pt" h="250pt" x="10pt" y="10pt">
+ <value>
+ <image contentType="image/gif">R0lGODdh+gD6AIABAP8AAP///ywAAAAA+gD6AAAC/oSPqcvtD6OctNqLs968+w+G4kiW5omm6sq27gvH8kzX9o3n+s73/g8MCofEovGITCqXzKbzCY1Kp9Sq9YrNarfcrvcLDovH5LL5jE6r1+y2+w2Py+f0uv2Oz+v3/L7/DxgoOEhYaHiImKi4yNjo+AgZKTlJWWl5iZmpucnZ6fkJGio6SlpqeoqaqrrK2ur6ChsrO0tba3uLm6u7y9vr+wscLDxMXGx8jJysvMzc7PwMHS09TV1tfY2drb3N3e39DR4uPk5ebn6Onq6+zt7u/g4fLz9PX29/j5+vv8/f7/8PMKDAgQQLGjyIMKHChQwbOnwIMaLEiRQrWryIMaPGV40cO3r8CDKkyJEkS5o8iTKlypUsW7p8CTOmzJk0a9q8iTOnzp08e/r8CTSo0KFEixo9ijSp0qVMmzp9CjWq1KlUq1q9ijWr1q1cu3r9Cjas2LFky14oAAA7</image>
+ </value>
+ <ui>
+ <imageEdit/>
+ </ui>
+ </draw>
+ </subform>
+ </subform>
+</template>
+endstream
+endobj
+{{include ../../xfa_locale_6_0.fragment}}
+{{include ../../xfa_postamble_7_0.fragment}}
+{{include ../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/xfa_specific/bug_1337_expected.pdf.0.png b/testing/resources/pixel/xfa_specific/bug_1337_expected.pdf.0.png
new file mode 100644
index 0000000..c0d8380
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/bug_1337_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/bug_983137_expected_skia.pdf.0.png b/testing/resources/pixel/xfa_specific/bug_983137_expected_skia.pdf.0.png
new file mode 100644
index 0000000..7c19b45
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/bug_983137_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/dynamic_list_box_allow_multiple_selection_expected_skia.pdf.0.png b/testing/resources/pixel/xfa_specific/dynamic_list_box_allow_multiple_selection_expected_skia.pdf.0.png
new file mode 100644
index 0000000..3ccc1a2
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/dynamic_list_box_allow_multiple_selection_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/dynamic_list_box_allow_multiple_selection_expected_skia_mac.pdf.0.png b/testing/resources/pixel/xfa_specific/dynamic_list_box_allow_multiple_selection_expected_skia_mac.pdf.0.png
new file mode 100644
index 0000000..eb303b2
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/dynamic_list_box_allow_multiple_selection_expected_skia_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/dynamic_password_field_background_fill_expected.pdf.0.png b/testing/resources/pixel/xfa_specific/dynamic_password_field_background_fill_expected.pdf.0.png
index d20d7ea..c1cebee 100644
--- a/testing/resources/pixel/xfa_specific/dynamic_password_field_background_fill_expected.pdf.0.png
+++ b/testing/resources/pixel/xfa_specific/dynamic_password_field_background_fill_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/dynamic_password_field_background_fill_expected_skia.pdf.0.png b/testing/resources/pixel/xfa_specific/dynamic_password_field_background_fill_expected_skia.pdf.0.png
new file mode 100644
index 0000000..072a537
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/dynamic_password_field_background_fill_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/dynamic_password_field_background_fill_expected_win.pdf.0.png b/testing/resources/pixel/xfa_specific/dynamic_password_field_background_fill_expected_win.pdf.0.png
deleted file mode 100644
index 734b7e4..0000000
--- a/testing/resources/pixel/xfa_specific/dynamic_password_field_background_fill_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/dynamic_table_color_and_width_expected_skia.pdf.0.png b/testing/resources/pixel/xfa_specific/dynamic_table_color_and_width_expected_skia.pdf.0.png
new file mode 100644
index 0000000..d443a35
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/dynamic_table_color_and_width_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/dynamic_table_color_and_width_expected_win.pdf.0.png b/testing/resources/pixel/xfa_specific/dynamic_table_color_and_width_expected_win.pdf.0.png
deleted file mode 100644
index eae383c..0000000
--- a/testing/resources/pixel/xfa_specific/dynamic_table_color_and_width_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/resolve_nodes_0_expected.pdf.0.png b/testing/resources/pixel/xfa_specific/resolve_nodes_0_expected.pdf.0.png
index a3d9830..adfabbc 100644
--- a/testing/resources/pixel/xfa_specific/resolve_nodes_0_expected.pdf.0.png
+++ b/testing/resources/pixel/xfa_specific/resolve_nodes_0_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/resolve_nodes_0_expected_skia.pdf.0.png b/testing/resources/pixel/xfa_specific/resolve_nodes_0_expected_skia.pdf.0.png
new file mode 100644
index 0000000..884a23f
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/resolve_nodes_0_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/standard_symbols_expected.pdf.0.png b/testing/resources/pixel/xfa_specific/standard_symbols_expected.pdf.0.png
new file mode 100644
index 0000000..62f3156
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/standard_symbols_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/standard_symbols_expected.pdf.1.png b/testing/resources/pixel/xfa_specific/standard_symbols_expected.pdf.1.png
new file mode 100644
index 0000000..e262e83
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/standard_symbols_expected.pdf.1.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/static_list_box_caption_expected_mac.pdf.0.png b/testing/resources/pixel/xfa_specific/static_list_box_caption_expected_mac.pdf.0.png
index 7cba6b9..b9066f4 100644
--- a/testing/resources/pixel/xfa_specific/static_list_box_caption_expected_mac.pdf.0.png
+++ b/testing/resources/pixel/xfa_specific/static_list_box_caption_expected_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/static_list_box_caption_expected_skia.pdf.0.png b/testing/resources/pixel/xfa_specific/static_list_box_caption_expected_skia.pdf.0.png
new file mode 100644
index 0000000..3beefe0
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/static_list_box_caption_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/static_list_box_caption_expected_win.pdf.0.png b/testing/resources/pixel/xfa_specific/static_list_box_caption_expected_win.pdf.0.png
deleted file mode 100644
index 71edafd..0000000
--- a/testing/resources/pixel/xfa_specific/static_list_box_caption_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_mac.pdf.0.png b/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_mac.pdf.0.png
index 58aee9e..bc87726 100644
--- a/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_mac.pdf.0.png
+++ b/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_mac.pdf.1.png b/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_mac.pdf.1.png
index ad36aa9..4dbd85a 100644
--- a/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_mac.pdf.1.png
+++ b/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_mac.pdf.1.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_skia.pdf.0.png b/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_skia.pdf.0.png
new file mode 100644
index 0000000..4b35140
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_skia.pdf.1.png b/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_skia.pdf.1.png
new file mode 100644
index 0000000..fd88db2
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_skia.pdf.1.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_win.pdf.0.png b/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_win.pdf.0.png
deleted file mode 100644
index ea939ac..0000000
--- a/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_win.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_win.pdf.1.png b/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_win.pdf.1.png
deleted file mode 100644
index c73f1a9..0000000
--- a/testing/resources/pixel/xfa_specific/static_password_field_rotate_expected_win.pdf.1.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/use_ahem/README.md b/testing/resources/pixel/xfa_specific/use_ahem/README.md
new file mode 100644
index 0000000..f3126e2
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/use_ahem/README.md
@@ -0,0 +1,2 @@
+The expected results in this directory should match those generated by running
+`pdfium_test` with the option `--font-dir=/path/to/pdfium/testing/resources/fonts`.
diff --git a/testing/resources/pixel/xfa_specific/use_ahem/bug_997412.in b/testing/resources/pixel/xfa_specific/use_ahem/bug_997412.in
new file mode 100644
index 0000000..ecda561
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/use_ahem/bug_997412.in
@@ -0,0 +1,35 @@
+{{header}}
+{{include ../../../xfa_catalog_1_0.fragment}}
+{{include ../../../xfa_object_2_0.fragment}}
+{{include ../../../xfa_preamble_3_0.fragment}}
+{{include ../../../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+ {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/3.3/">
+ <subform layout="rl-tb" name="subform1">
+ <pageSet>
+ <pageArea name="Page1" id="Page1">
+ <contentArea x="18pt" y="18pt" w="612pt" h="792pt"/>
+ <medium stock="default" short="612pt" long="792pt"/>
+ </pageArea>
+ </pageSet>
+ <field h="3000pt" name="Field1">
+ <font typeface="Ahem" size="20pt"/>
+ <value>
+ <text>foo
+<!-- Intentionally formatted to trigger bug //--></text>
+ </value>
+ </field>
+ </subform>
+</template>
+endstream
+endobj
+{{include ../../../xfa_locale_6_0.fragment}}
+{{include ../../../xfa_postamble_7_0.fragment}}
+{{include ../../../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/pixel/xfa_specific/use_ahem/bug_997412_expected.pdf.0.png b/testing/resources/pixel/xfa_specific/use_ahem/bug_997412_expected.pdf.0.png
new file mode 100644
index 0000000..08c11b0
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/use_ahem/bug_997412_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/use_ahem/bug_997412_expected.pdf.1.png b/testing/resources/pixel/xfa_specific/use_ahem/bug_997412_expected.pdf.1.png
new file mode 100644
index 0000000..7734ff7
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/use_ahem/bug_997412_expected.pdf.1.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/use_ahem/bug_997412_expected_skia.pdf.1.png b/testing/resources/pixel/xfa_specific/use_ahem/bug_997412_expected_skia.pdf.1.png
new file mode 100644
index 0000000..d4da28c
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/use_ahem/bug_997412_expected_skia.pdf.1.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/use_ahem/xfa_example_expected_skia.pdf.0.png b/testing/resources/pixel/xfa_specific/use_ahem/xfa_example_expected_skia.pdf.0.png
new file mode 100644
index 0000000..5a3eb9f
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/use_ahem/xfa_example_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/use_ahem/xfa_textfield_expected_skia.pdf.0.png b/testing/resources/pixel/xfa_specific/use_ahem/xfa_textfield_expected_skia.pdf.0.png
new file mode 100644
index 0000000..ad18d26
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/use_ahem/xfa_textfield_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_bmp_image.in b/testing/resources/pixel/xfa_specific/xfa_bmp_image.in
index 9b444f1..dfd9026 100644
--- a/testing/resources/pixel/xfa_specific/xfa_bmp_image.in
+++ b/testing/resources/pixel/xfa_specific/xfa_bmp_image.in
@@ -18,7 +18,7 @@
<subform w="576pt" h="756pt" name="Page1">
<field name="ImageField1" w="250pt" h="250pt">
<value>
- <image contentType="image/bmp">Qk0qHgAAAAAAAHoAAABsAAAAMgAAADIAAAABABgAAAAAALAdAAATCwAAEwsAAAAAAAAAAAAAQkdScwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAAAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAAAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAAAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAAAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAAAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAAAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAAAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAAAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAAAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAAAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAAAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAAAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAAAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAAAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAAAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAAAAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAD/AAAAAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAP8AAAAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA/wAA</image>
+ <image contentType="image/bmp">Qk1mCgAAAAAAAD4AAAAoAAAAMgAAADIAAAABAAgAAAAAACgKAAATCwAAEwsAAAIAAAACAAAAAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==</image>
</value>
<border>
<edge thickness="0.254mm"/>
diff --git a/testing/resources/pixel/xfa_specific/xfa_bmp_image_expected_skia.pdf.0.png b/testing/resources/pixel/xfa_specific/xfa_bmp_image_expected_skia.pdf.0.png
new file mode 100644
index 0000000..2f77624
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_bmp_image_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_gif_image_expected_skia.pdf.0.png b/testing/resources/pixel/xfa_specific/xfa_gif_image_expected_skia.pdf.0.png
new file mode 100644
index 0000000..2f77624
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_gif_image_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_jpg_image_expected_skia.pdf.0.png b/testing/resources/pixel/xfa_specific/xfa_jpg_image_expected_skia.pdf.0.png
new file mode 100644
index 0000000..70dcd8e
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_jpg_image_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_node_caption_expected.pdf.0.png b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected.pdf.0.png
index 6855361..4bfe7c8 100644
--- a/testing/resources/pixel/xfa_specific/xfa_node_caption_expected.pdf.0.png
+++ b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_node_caption_expected.pdf.1.png b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected.pdf.1.png
index 1f5c1bb..44aa0b1 100644
--- a/testing/resources/pixel/xfa_specific/xfa_node_caption_expected.pdf.1.png
+++ b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected.pdf.1.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_mac.pdf.0.png b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_mac.pdf.0.png
new file mode 100644
index 0000000..4912da0
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_mac.pdf.1.png b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_mac.pdf.1.png
new file mode 100644
index 0000000..427b5b3
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_mac.pdf.1.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia.pdf.0.png b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia.pdf.0.png
new file mode 100644
index 0000000..fe509a8
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia.pdf.1.png b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia.pdf.1.png
new file mode 100644
index 0000000..e720b9b
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia.pdf.1.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia_mac.pdf.0.png b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia_mac.pdf.0.png
new file mode 100644
index 0000000..384294c
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia_mac.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia_mac.pdf.1.png b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia_mac.pdf.1.png
new file mode 100644
index 0000000..3366bc0
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia_mac.pdf.1.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia_win.pdf.0.png b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia_win.pdf.0.png
new file mode 100644
index 0000000..54f04d8
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia_win.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia_win.pdf.1.png b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia_win.pdf.1.png
new file mode 100644
index 0000000..7691fb1
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_skia_win.pdf.1.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_win.pdf.0.png b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_win.pdf.0.png
new file mode 100644
index 0000000..ffa36fe
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_win.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_win.pdf.1.png b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_win.pdf.1.png
new file mode 100644
index 0000000..2448322
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_node_caption_expected_win.pdf.1.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_png_image_expected_skia.pdf.0.png b/testing/resources/pixel/xfa_specific/xfa_png_image_expected_skia.pdf.0.png
new file mode 100644
index 0000000..2f77624
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_png_image_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_rectangle_node_expected_skia.pdf.0.png b/testing/resources/pixel/xfa_specific/xfa_rectangle_node_expected_skia.pdf.0.png
new file mode 100644
index 0000000..99cee53
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_rectangle_node_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_tiff_deflate_image.in b/testing/resources/pixel/xfa_specific/xfa_tiff_deflate_image.in
deleted file mode 100644
index 797f84c..0000000
--- a/testing/resources/pixel/xfa_specific/xfa_tiff_deflate_image.in
+++ /dev/null
@@ -1,39 +0,0 @@
-{{header}}
-{{include ../../xfa_catalog_1_0.fragment}}
-{{include ../../xfa_object_2_0.fragment}}
-{{include ../../xfa_preamble_3_0.fragment}}
-{{include ../../xfa_config_4_0.fragment}}
-{{object 5 0}} <<
- {{streamlen}}
->>
-stream
-<template xmlns="http://www.xfa.org/schema/xfa-template/3.3/">
- <subform name="form1" layout="tb" locale="en_US" restoreState="auto">
- <pageSet>
- <pageArea name="Page1" id="Page1">
- <contentArea x="18pt" y="18pt" w="612pt" h="792pt"/>
- <medium stock="default" short="612pt" long="792pt"/>
- </pageArea>
- </pageSet>
- <subform w="576pt" h="756pt" name="Page1">
- <field name="ImageField1" w="250pt" h="250pt">
- <value>
- <image contentType="image/tiff">SUkqAAQDAAB4nO3SMQEAMAyAsPo33brYDhIFHOxAxf4OgGfcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ2OAxGIP8F4nO3SMQEAMAyAsPo33brYDhIFHOxAxf4OgGfcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ2OAxGIP8F4nO3SMQEAMAyAsPo33brYDhIFHOxAxf4OgGfcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ2OAxGIP8F4nO3SMQEAMAyAsPo33brYDhIFHOxAxf4OgGfcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjrcTofb6XA7HW6nw+10uJ0Ot9PhdjoO69g5xxIA/gAEAAEAAAAAAAAAAAEDAAEAAAD6AAAAAQEDAAEAAAD6AAAAAgEDAAMAAADyAwAAAwEDAAEAAAAIAAAABgEDAAEAAAACAAAADQECAEIAAAAYBAAADgECABIAAABaBAAAEQEEAAQAAAAIBAAAEgEDAAEAAAABAAAAFQEDAAEAAAADAAAAFgEDAAEAAABAAAAAFwEEAAQAAAD4AwAAGgEFAAEAAADiAwAAGwEFAAEAAADqAwAAHAEDAAEAAAABAAAAKAEDAAEAAAACAAAAPQEDAAEAAAACAAAAAAAAAEgAAAABAAAASAAAAAEAAAAIAAgACADDAAAAwwAAAMMAAACzAAAACAAAAMsAAACOAQAAUQIAAC91c3IvbG9jYWwvZ29vZ2xlL2hvbWUvcmhhcnJpc29uL1BpY3R1cmVzL1JlZF9TcXVhcmVfZGVmbGF0ZS50aWZmAENyZWF0ZWQgd2l0aCBHSU1QAA==</image>
- </value>
- <border>
- <edge thickness="0.254mm"/>
- <corner thickness="0.254mm"/>
- </border>
- </field>
- </subform>
- </subform>
-</template>
-endstream
-endobj
-{{include ../../xfa_locale_6_0.fragment}}
-{{include ../../xfa_postamble_7_0.fragment}}
-{{include ../../xfa_pages_8_0.fragment}}
-{{xref}}
-{{trailer}}
-{{startxref}}
-%%EOF
diff --git a/testing/resources/pixel/xfa_specific/xfa_tiff_deflate_image_expected.pdf.0.png b/testing/resources/pixel/xfa_specific/xfa_tiff_deflate_image_expected.pdf.0.png
deleted file mode 100644
index 5204fd7..0000000
--- a/testing/resources/pixel/xfa_specific/xfa_tiff_deflate_image_expected.pdf.0.png
+++ /dev/null
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_tiff_image_expected_skia.pdf.0.png b/testing/resources/pixel/xfa_specific/xfa_tiff_image_expected_skia.pdf.0.png
new file mode 100644
index 0000000..2f77624
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_tiff_image_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_tiff_lzw_image_expected_skia.pdf.0.png b/testing/resources/pixel/xfa_specific/xfa_tiff_lzw_image_expected_skia.pdf.0.png
new file mode 100644
index 0000000..2f77624
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_tiff_lzw_image_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/pixel/xfa_specific/xfa_tiff_packbits_image_expected_skia.pdf.0.png b/testing/resources/pixel/xfa_specific/xfa_tiff_packbits_image_expected_skia.pdf.0.png
new file mode 100644
index 0000000..2f77624
--- /dev/null
+++ b/testing/resources/pixel/xfa_specific/xfa_tiff_packbits_image_expected_skia.pdf.0.png
Binary files differ
diff --git a/testing/resources/polygon_annot.in b/testing/resources/polygon_annot.in
new file mode 100644
index 0000000..069ddc1
--- /dev/null
+++ b/testing/resources/polygon_annot.in
@@ -0,0 +1,48 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 612 792]
+ /Annots [
+ 4 0 R 5 0 R
+ ]
+ /Tabs /R
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Annot
+ /Subtype /Polygon
+ /NM (Polygon-1)
+ /F 4
+ /Vertices [159 296 350 411 472 243.42]
+ /P 3 0 R
+ /C [1 0.90196 0]
+ /Rect [293 530 349 542]
+>>
+endobj
+{{object 5 0}} <<
+ /Type /Annot
+ /Subtype /Polygon
+ /NM (Polygon-2)
+ /F 4
+ /Vertices [259 396 450 511 572 343 42]
+ /P 3 0 R
+ /C [1 0.90196 0]
+ /Rect [293 530 349 542]
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/polygon_annot.pdf b/testing/resources/polygon_annot.pdf
new file mode 100644
index 0000000..8efaa27
--- /dev/null
+++ b/testing/resources/polygon_annot.pdf
@@ -0,0 +1,60 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /MediaBox [0 0 612 792]
+ /Annots [
+ 4 0 R 5 0 R
+ ]
+ /Tabs /R
+>>
+endobj
+4 0 obj <<
+ /Type /Annot
+ /Subtype /Polygon
+ /NM (Polygon-1)
+ /F 4
+ /Vertices [159 296 350 411 472 243.42]
+ /P 3 0 R
+ /C [1 0.90196 0]
+ /Rect [293 530 349 542]
+>>
+endobj
+5 0 obj <<
+ /Type /Annot
+ /Subtype /Polygon
+ /NM (Polygon-2)
+ /F 4
+ /Vertices [259 396 450 511 572 343 42]
+ /P 3 0 R
+ /C [1 0.90196 0]
+ /Rect [293 530 349 542]
+>>
+endobj
+xref
+0 6
+0000000000 65535 f
+0000000015 00000 n
+0000000068 00000 n
+0000000131 00000 n
+0000000251 00000 n
+0000000429 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 6
+>>
+startxref
+607
+%%EOF
diff --git a/testing/resources/rectangles.in b/testing/resources/rectangles.in
index 49932ff..7334516 100644
--- a/testing/resources/rectangles.in
+++ b/testing/resources/rectangles.in
@@ -6,9 +6,9 @@
endobj
{{object 2 0}} <<
/Type /Pages
- /MediaBox [ 0 0 200 300 ]
+ /MediaBox [0 0 200 300]
/Count 1
- /Kids [ 3 0 R ]
+ /Kids [3 0 R]
>>
endobj
{{object 3 0}} <<
@@ -18,6 +18,7 @@
>>
endobj
{{object 4 0}} <<
+ {{streamlen}}
>>
stream
q
diff --git a/testing/resources/rectangles.pdf b/testing/resources/rectangles.pdf
index 7bad251..6911d22 100644
--- a/testing/resources/rectangles.pdf
+++ b/testing/resources/rectangles.pdf
@@ -7,9 +7,9 @@
endobj
2 0 obj <<
/Type /Pages
- /MediaBox [ 0 0 200 300 ]
+ /MediaBox [0 0 200 300]
/Count 1
- /Kids [ 3 0 R ]
+ /Kids [3 0 R]
>>
endobj
3 0 obj <<
@@ -19,6 +19,7 @@
>>
endobj
4 0 obj <<
+ /Length 188
>>
stream
q
@@ -42,9 +43,12 @@
0000000000 65535 f
0000000015 00000 n
0000000068 00000 n
-0000000161 00000 n
-0000000230 00000 n
-trailer<< /Root 1 0 R /Size 5 >>
+0000000157 00000 n
+0000000226 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 5
+>>
startxref
-456
+466
%%EOF
diff --git a/testing/resources/redact_annot.in b/testing/resources/redact_annot.in
new file mode 100644
index 0000000..99b7b4e
--- /dev/null
+++ b/testing/resources/redact_annot.in
@@ -0,0 +1,44 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /MediaBox [0 0 612 792]
+ /Annots [
+ 5 0 R
+ ]
+ /Tabs /R
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+endstream
+endobj
+{{object 5 0}} <<
+ /Type /Annot
+ /Subtype /Redact
+ /NM (Redact-1)
+ /F 4
+ /QuadPoints [293 542 349 542 293 530 349 530]
+ /P 3 0 R
+ /C [1 0.90196 0]
+ /Rect [293 530 349 542]
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/redact_annot.pdf b/testing/resources/redact_annot.pdf
new file mode 100644
index 0000000..76c2607
--- /dev/null
+++ b/testing/resources/redact_annot.pdf
@@ -0,0 +1,56 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /MediaBox [0 0 612 792]
+ /Annots [
+ 5 0 R
+ ]
+ /Tabs /R
+>>
+endobj
+4 0 obj <<
+ /Length 0
+>>
+stream
+endstream
+endobj
+5 0 obj <<
+ /Type /Annot
+ /Subtype /Redact
+ /NM (Redact-1)
+ /F 4
+ /QuadPoints [293 542 349 542 293 530 349 530]
+ /P 3 0 R
+ /C [1 0.90196 0]
+ /Rect [293 530 349 542]
+>>
+endobj
+xref
+0 6
+0000000000 65535 f
+0000000015 00000 n
+0000000068 00000 n
+0000000131 00000 n
+0000000263 00000 n
+0000000313 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 6
+>>
+startxref
+496
+%%EOF
diff --git a/testing/resources/rotated_image.in b/testing/resources/rotated_image.in
new file mode 100644
index 0000000..cf82fe8
--- /dev/null
+++ b/testing/resources/rotated_image.in
@@ -0,0 +1,52 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /MediaBox [0 0 200 200]
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /Resources <<
+ /XObject <<
+ /Img 5 0 R
+ >>
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+30 -30 40 40 100 100 cm
+/Img Do
+Q
+endstream
+endobj
+{{object 5 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /Width 50
+ /Height 50
+ /BitsPerComponent 8
+ /ColorSpace /DeviceRGB
+ /Filter [/ASCIIHexDecode /FlateDecode]
+ {{streamlen}}
+>>
+stream
+789cedc2310d00000c03a07f2aaab3ea7bcf03842655555555555555f5bf01cc7818dc
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/rotated_image.pdf b/testing/resources/rotated_image.pdf
new file mode 100644
index 0000000..5bb1836
--- /dev/null
+++ b/testing/resources/rotated_image.pdf
@@ -0,0 +1,64 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /MediaBox [0 0 200 200]
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /Resources <<
+ /XObject <<
+ /Img 5 0 R
+ >>
+ >>
+>>
+endobj
+4 0 obj <<
+ /Length 36
+>>
+stream
+q
+30 -30 40 40 100 100 cm
+/Img Do
+Q
+endstream
+endobj
+5 0 obj <<
+ /Type /XObject
+ /Subtype /Image
+ /Width 50
+ /Height 50
+ /BitsPerComponent 8
+ /ColorSpace /DeviceRGB
+ /Filter [/ASCIIHexDecode /FlateDecode]
+ /Length 71
+>>
+stream
+789cedc2310d00000c03a07f2aaab3ea7bcf03842655555555555555f5bf01cc7818dc
+endstream
+endobj
+xref
+0 6
+0000000000 65535 f
+0000000015 00000 n
+0000000068 00000 n
+0000000157 00000 n
+0000000287 00000 n
+0000000374 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 6
+>>
+startxref
+644
+%%EOF
diff --git a/testing/resources/signature_no_sub_filter.in b/testing/resources/signature_no_sub_filter.in
new file mode 100644
index 0000000..5c1d1d8
--- /dev/null
+++ b/testing/resources/signature_no_sub_filter.in
@@ -0,0 +1,100 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /MediaBox [0 0 200 300]
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+0 0 0 rg
+0 290 10 10 re B*
+10 150 50 30 re B*
+0 0 1 rg
+190 290 10 10 re B*
+70 232 50 30 re B*
+0 1 0 rg
+190 0 10 10 re B*
+130 150 50 30 re B*
+1 0 0 rg
+0 0 10 10 re B*
+70 67 50 30 re B*
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
+
+%% First incremental update adds an initial signature and update objects to
+%% refer to it.
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /AcroForm <<
+ /Fields [7 0 R]
+ /SigFlags 3
+ >>
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /Annots [7 0 R]
+>>
+endobj
+%% ByteRange is a pairs of integers (starting byte offset, length in bytes)
+{{object 5 0}} <<
+ /Type /Sig
+ /Filter /Adobe.PPKMS
+ % Intentionally no /SubFilter
+ /ByteRange [0 10 30 10]
+ /Contents <308006092A864886F70D010702A0803080020101>
+ /M (D:20200624093114+02'00')
+>>
+endobj
+{{object 6 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /BBox [0 0 0 0]
+ /Length 0
+>>
+stream
+endstream
+endobj
+{{object 7 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Sig
+ /F 132
+ /Rect [0 0 0 0]
+ /P 3 0 R
+ /T (Signature1)
+ /V 5 0 R
+ /DV 5 0 R
+ /AP <<
+ /N 6 0 R
+ >>
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/signature_no_sub_filter.pdf b/testing/resources/signature_no_sub_filter.pdf
new file mode 100644
index 0000000..ed305ef
--- /dev/null
+++ b/testing/resources/signature_no_sub_filter.pdf
@@ -0,0 +1,124 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /MediaBox [0 0 200 300]
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+>>
+endobj
+4 0 obj <<
+ /Length 188
+>>
+stream
+q
+0 0 0 rg
+0 290 10 10 re B*
+10 150 50 30 re B*
+0 0 1 rg
+190 290 10 10 re B*
+70 232 50 30 re B*
+0 1 0 rg
+190 0 10 10 re B*
+130 150 50 30 re B*
+1 0 0 rg
+0 0 10 10 re B*
+70 67 50 30 re B*
+Q
+endstream
+endobj
+xref
+0 5
+0000000000 65535 f
+0000000015 00000 n
+0000000068 00000 n
+0000000157 00000 n
+0000000226 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 5
+>>
+startxref
+466
+%%EOF
+
+%% First incremental update adds an initial signature and update objects to
+%% refer to it.
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /AcroForm <<
+ /Fields [7 0 R]
+ /SigFlags 3
+ >>
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /Annots [7 0 R]
+>>
+endobj
+%% ByteRange is a pairs of integers (starting byte offset, length in bytes)
+5 0 obj <<
+ /Type /Sig
+ /Filter /Adobe.PPKMS
+ % Intentionally no /SubFilter
+ /ByteRange [0 10 30 10]
+ /Contents <308006092A864886F70D010702A0803080020101>
+ /M (D:20200624093114+02'00')
+>>
+endobj
+6 0 obj <<
+ /Type /XObject
+ /Subtype /Form
+ /BBox [0 0 0 0]
+ /Length 0
+>>
+stream
+endstream
+endobj
+7 0 obj <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Sig
+ /F 132
+ /Rect [0 0 0 0]
+ /P 3 0 R
+ /T (Signature1)
+ /V 5 0 R
+ /DV 5 0 R
+ /AP <<
+ /N 6 0 R
+ >>
+>>
+endobj
+xref
+0 8
+0000000000 65535 f
+0000000726 00000 n
+0000000068 00000 n
+0000000835 00000 n
+0000000226 00000 n
+0000000998 00000 n
+0000001199 00000 n
+0000001301 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 8
+>>
+startxref
+1473
+%%EOF
diff --git a/testing/resources/signature_reason.in b/testing/resources/signature_reason.in
new file mode 100644
index 0000000..2c5d0f9
--- /dev/null
+++ b/testing/resources/signature_reason.in
@@ -0,0 +1,101 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /MediaBox [0 0 200 300]
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+0 0 0 rg
+0 290 10 10 re B*
+10 150 50 30 re B*
+0 0 1 rg
+190 290 10 10 re B*
+70 232 50 30 re B*
+0 1 0 rg
+190 0 10 10 re B*
+130 150 50 30 re B*
+1 0 0 rg
+0 0 10 10 re B*
+70 67 50 30 re B*
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
+
+%% First incremental update adds an initial signature and update objects to
+%% refer to it.
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /AcroForm <<
+ /Fields [7 0 R]
+ /SigFlags 3
+ >>
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /Annots [7 0 R]
+>>
+endobj
+%% ByteRange is a pairs of integers (starting byte offset, length in bytes)
+{{object 5 0}} <<
+ /Type /Sig
+ /Filter /Adobe.PPKMS
+ /SubFilter /ETSI.CAdES.detached
+ /ByteRange [0 10 30 10]
+ /Contents <308006092A864886F70D010702A0803080020101>
+ /M (D:20200624093114+02'00')
+ /Reason <FEFF007400650073007400200072006500610073006F006E>
+>>
+endobj
+{{object 6 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /BBox [0 0 0 0]
+ /Length 0
+>>
+stream
+endstream
+endobj
+{{object 7 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Sig
+ /F 132
+ /Rect [0 0 0 0]
+ /P 3 0 R
+ /T (Signature1)
+ /V 5 0 R
+ /DV 5 0 R
+ /AP <<
+ /N 6 0 R
+ >>
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/signature_reason.pdf b/testing/resources/signature_reason.pdf
new file mode 100644
index 0000000..3ad0205
--- /dev/null
+++ b/testing/resources/signature_reason.pdf
@@ -0,0 +1,125 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /MediaBox [0 0 200 300]
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+>>
+endobj
+4 0 obj <<
+ /Length 188
+>>
+stream
+q
+0 0 0 rg
+0 290 10 10 re B*
+10 150 50 30 re B*
+0 0 1 rg
+190 290 10 10 re B*
+70 232 50 30 re B*
+0 1 0 rg
+190 0 10 10 re B*
+130 150 50 30 re B*
+1 0 0 rg
+0 0 10 10 re B*
+70 67 50 30 re B*
+Q
+endstream
+endobj
+xref
+0 5
+0000000000 65535 f
+0000000015 00000 n
+0000000068 00000 n
+0000000157 00000 n
+0000000226 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 5
+>>
+startxref
+466
+%%EOF
+
+%% First incremental update adds an initial signature and update objects to
+%% refer to it.
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /AcroForm <<
+ /Fields [7 0 R]
+ /SigFlags 3
+ >>
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /Annots [7 0 R]
+>>
+endobj
+%% ByteRange is a pairs of integers (starting byte offset, length in bytes)
+5 0 obj <<
+ /Type /Sig
+ /Filter /Adobe.PPKMS
+ /SubFilter /ETSI.CAdES.detached
+ /ByteRange [0 10 30 10]
+ /Contents <308006092A864886F70D010702A0803080020101>
+ /M (D:20200624093114+02'00')
+ /Reason <FEFF007400650073007400200072006500610073006F006E>
+>>
+endobj
+6 0 obj <<
+ /Type /XObject
+ /Subtype /Form
+ /BBox [0 0 0 0]
+ /Length 0
+>>
+stream
+endstream
+endobj
+7 0 obj <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Sig
+ /F 132
+ /Rect [0 0 0 0]
+ /P 3 0 R
+ /T (Signature1)
+ /V 5 0 R
+ /DV 5 0 R
+ /AP <<
+ /N 6 0 R
+ >>
+>>
+endobj
+xref
+0 8
+0000000000 65535 f
+0000000726 00000 n
+0000000068 00000 n
+0000000835 00000 n
+0000000226 00000 n
+0000000998 00000 n
+0000001262 00000 n
+0000001364 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 8
+>>
+startxref
+1536
+%%EOF
diff --git a/testing/resources/simple_xfa.in b/testing/resources/simple_xfa.in
index b20c746..d3c2d00 100644
--- a/testing/resources/simple_xfa.in
+++ b/testing/resources/simple_xfa.in
@@ -12,6 +12,7 @@
<pageSet>
<pageArea name="Page1" id="Page1">
<contentArea x="0.25in" y="0.25in" w="8in" h="10.5in" />
+ <medium long="11in" short="8.5in" stock="letter"/>
</pageArea>
</pageSet>
<field name="TextField1" y="31.75mm" x="44.45mm" w="114.291mm" h="12.7mm">
diff --git a/testing/resources/simple_xfa.pdf b/testing/resources/simple_xfa.pdf
index 89f9aea..8ff5b12 100644
--- a/testing/resources/simple_xfa.pdf
+++ b/testing/resources/simple_xfa.pdf
@@ -72,7 +72,7 @@
endstream
endobj
5 0 obj <<
- /Length 482
+ /Length 541
>>
stream
<template xmlns="http://www.xfa.org/schema/xfa-template/3.3/">
@@ -80,6 +80,7 @@
<pageSet>
<pageArea name="Page1" id="Page1">
<contentArea x="0.25in" y="0.25in" w="8in" h="10.5in" />
+ <medium long="11in" short="8.5in" stock="letter"/>
</pageArea>
</pageSet>
<field name="TextField1" y="31.75mm" x="44.45mm" w="114.291mm" h="12.7mm">
@@ -215,7 +216,7 @@
endobj
9 0 obj <<
/Type /Page
- /Parent 2 0 R
+ /Parent 8 0 R
/MediaBox [0 0 612 792]
>>
endobj
@@ -227,14 +228,14 @@
0000000358 00000 n
0000000534 00000 n
0000001228 00000 n
-0000001762 00000 n
-0000005270 00000 n
-0000005332 00000 n
-0000005395 00000 n
+0000001821 00000 n
+0000005329 00000 n
+0000005391 00000 n
+0000005454 00000 n
trailer <<
/Root 1 0 R
/Size 10
>>
startxref
-5472
+5531
%%EOF
diff --git a/testing/resources/split_streams.in b/testing/resources/split_streams.in
index b134769..729c67a 100644
--- a/testing/resources/split_streams.in
+++ b/testing/resources/split_streams.in
@@ -117,7 +117,7 @@
{{xref}}
trailer <<
/Root 1 0 R
- /Size 15
+ {{trailersize}}
/ID [<f341ae654a77acd5065a7645e596e6e6><bc37298a3f87f479229bce997ca791f7>]
>>
{{startxref}}
diff --git a/testing/resources/tagged_actual_text.in b/testing/resources/tagged_actual_text.in
new file mode 100644
index 0000000..9bb917b
--- /dev/null
+++ b/testing/resources/tagged_actual_text.in
@@ -0,0 +1,162 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /StructTreeRoot 8 0 R
+ /Lang (en-US)
+ /MarkInfo <<
+ /Marked true
+ >>
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /MediaBox [0 0 612 792]
+ /Group <<
+ /CS /DeviceRGB
+ /I true
+ /S /Transparency
+ >>
+ /Resources <<
+ /ProcSet [/PDF /ImageC /ImageI /ImageB]
+ /XObject <<
+ /Tr8 5 0 R
+ /Im7 6 0 R
+ >>
+ /ExtGState <<
+ /EGS9 7 0 R
+ >>
+ >>
+ /StructParents 0
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+0.1 w
+/Artifact
+BMC
+q
+0 0 612 792 re
+W* n
+EMC
+/Figure<</MCID 0>>
+BDC
+Q
+q
+281 685.3 50 50 re
+W* n
+q
+49.9 0 0 50 281.1 685.4 cm
+/Im7 Do
+Q
+EMC
+Q
+q
+EGS9 gs /Tr8 Do
+Q
+endstream
+endobj
+{{object 5 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /BBox [-140 395 753 395.1]
+ /Group <<
+ /CS /DeviceRGB
+ /K true
+ /S /Transparency
+ >>
+ {{streamlen}}
+>>
+stream
+endstream
+endobj
+{{object 6 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /Width 50
+ /Height 50
+ /BitsPerComponent 8
+ /ColorSpace /DeviceRGB
+ /Filter [/ASCIIHexDecode /FlateDecode]
+ {{streamlen}}
+>>
+stream
+789cedc13101000000c2a0f54fed6f06a00000000000000078031d4c0001
+endstream
+endobj
+{{object 7 0}} <<
+ /ca 0.5
+ /CA 0.5
+>>
+endobj
+{{object 8 0}} <<
+ /Type /StructTreeRoot
+ /ParentTree 9 0 R
+ /K [11 0 R]
+ /RoleMap <<
+ /Document /Document
+ /Standard /P
+ /Figure /Figure
+ >>
+>>
+endobj
+{{object 9 0}} <<
+ /Nums [0 [10 0 R]]
+>>
+endobj
+{{object 10 0}} <<
+ /Type /StructElem
+ /S /Figure
+ /A 13 0 R
+ /K [0]
+ /P 12 0 R
+ /ActualText <feff00410063007400750061006c00200054006500780074>
+ /Pg 3 0 R
+>>
+endobj
+{{object 11 0}} <<
+ /Type /StructElem
+ /S /Document
+ /K [12 0 R]
+ /P 8 0 R
+ /T (TitleText)
+ /Pg 3 0 R
+>>
+endobj
+{{object 12 0}} <<
+ /Type /StructElem
+ /S /Standard
+ /A 14 0 R
+ /K [10 0 R]
+ /P 11 0 R
+ /T <feff00730079006d0062006f006c003a0020003100300030006b>
+ /Pg 3 0 R
+>>
+endobj
+{{object 13 0}} <<
+ /O /Layout
+ /Placement /Block
+ /BBox [281.1 685.3 331.1 735.3]
+ /Width 99.9
+ /Height 99.9
+>>
+endobj
+{{object 14 0}} <<
+ /O /Layout
+ /Placement /Block
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/tagged_actual_text.pdf b/testing/resources/tagged_actual_text.pdf
new file mode 100644
index 0000000..634e333
--- /dev/null
+++ b/testing/resources/tagged_actual_text.pdf
@@ -0,0 +1,183 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /StructTreeRoot 8 0 R
+ /Lang (en-US)
+ /MarkInfo <<
+ /Marked true
+ >>
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /MediaBox [0 0 612 792]
+ /Group <<
+ /CS /DeviceRGB
+ /I true
+ /S /Transparency
+ >>
+ /Resources <<
+ /ProcSet [/PDF /ImageC /ImageI /ImageB]
+ /XObject <<
+ /Tr8 5 0 R
+ /Im7 6 0 R
+ >>
+ /ExtGState <<
+ /EGS9 7 0 R
+ >>
+ >>
+ /StructParents 0
+>>
+endobj
+4 0 obj <<
+ /Length 162
+>>
+stream
+0.1 w
+/Artifact
+BMC
+q
+0 0 612 792 re
+W* n
+EMC
+/Figure<</MCID 0>>
+BDC
+Q
+q
+281 685.3 50 50 re
+W* n
+q
+49.9 0 0 50 281.1 685.4 cm
+/Im7 Do
+Q
+EMC
+Q
+q
+EGS9 gs /Tr8 Do
+Q
+endstream
+endobj
+5 0 obj <<
+ /Type /XObject
+ /Subtype /Form
+ /BBox [-140 395 753 395.1]
+ /Group <<
+ /CS /DeviceRGB
+ /K true
+ /S /Transparency
+ >>
+ /Length 0
+>>
+stream
+endstream
+endobj
+6 0 obj <<
+ /Type /XObject
+ /Subtype /Image
+ /Width 50
+ /Height 50
+ /BitsPerComponent 8
+ /ColorSpace /DeviceRGB
+ /Filter [/ASCIIHexDecode /FlateDecode]
+ /Length 61
+>>
+stream
+789cedc13101000000c2a0f54fed6f06a00000000000000078031d4c0001
+endstream
+endobj
+7 0 obj <<
+ /ca 0.5
+ /CA 0.5
+>>
+endobj
+8 0 obj <<
+ /Type /StructTreeRoot
+ /ParentTree 9 0 R
+ /K [11 0 R]
+ /RoleMap <<
+ /Document /Document
+ /Standard /P
+ /Figure /Figure
+ >>
+>>
+endobj
+9 0 obj <<
+ /Nums [0 [10 0 R]]
+>>
+endobj
+10 0 obj <<
+ /Type /StructElem
+ /S /Figure
+ /A 13 0 R
+ /K [0]
+ /P 12 0 R
+ /ActualText <feff00410063007400750061006c00200054006500780074>
+ /Pg 3 0 R
+>>
+endobj
+11 0 obj <<
+ /Type /StructElem
+ /S /Document
+ /K [12 0 R]
+ /P 8 0 R
+ /T (TitleText)
+ /Pg 3 0 R
+>>
+endobj
+12 0 obj <<
+ /Type /StructElem
+ /S /Standard
+ /A 14 0 R
+ /K [10 0 R]
+ /P 11 0 R
+ /T <feff00730079006d0062006f006c003a0020003100300030006b>
+ /Pg 3 0 R
+>>
+endobj
+13 0 obj <<
+ /O /Layout
+ /Placement /Block
+ /BBox [281.1 685.3 331.1 735.3]
+ /Width 99.9
+ /Height 99.9
+>>
+endobj
+14 0 obj <<
+ /O /Layout
+ /Placement /Block
+>>
+endobj
+xref
+0 15
+0000000000 65535 f
+0000000015 00000 n
+0000000145 00000 n
+0000000208 00000 n
+0000000556 00000 n
+0000000770 00000 n
+0000000952 00000 n
+0000001212 00000 n
+0000001253 00000 n
+0000001412 00000 n
+0000001454 00000 n
+0000001619 00000 n
+0000001730 00000 n
+0000001897 00000 n
+0000002015 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 15
+>>
+startxref
+2070
+%%EOF
diff --git a/testing/resources/tagged_marked_content.in b/testing/resources/tagged_marked_content.in
new file mode 100644
index 0000000..a8ea64b
--- /dev/null
+++ b/testing/resources/tagged_marked_content.in
@@ -0,0 +1,140 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /StructTreeRoot 7 0 R
+ /MarkInfo <<
+ /Type /MarkInfo
+ /Marked true
+ >>
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /StructParents 0
+ /Annots [4 0 R]
+ /Contents 5 0 R
+ /MediaBox [0 0 612 792]
+ /Resources <<
+ /ProcSet [/PDF /Text]
+ /Font <<
+ /F4 6 0 R
+ >>
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Annot
+ /Subtype /Link
+ /Border [0 0 0]
+ /Dest /top
+ /F 4
+ /Rect [20 46 68 61]
+>>
+endobj
+{{object 5 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+BT
+/P <</MCID 0 >>BDC
+/F4 16 Tf
+20 650 Td
+(Top Left) Tj
+EMC
+ET
+BT
+/P <</MCID 1 >>BDC
+/F4 16 Tf
+20 50 Td
+(Bottom Left) Tj
+EMC
+ET
+BT
+/P <</MCID 2 >>BDC
+/F4 16 Tf
+400 50 Td
+(Bottom Right) Tj
+EMC
+ET
+BT
+/P <</MCID 3 >>BDC
+/F4 16 Tf
+400 650 Td
+(Top Right) Tj
+EMC
+ET
+Q
+endstream
+endobj
+{{object 6 0}} <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Times-Roman
+>>
+endobj
+{{object 7 0}} <<
+ /Type /StructTreeRoot
+ /K [9 0 R 10 0 R 11 0 R 12 0 R]
+ /ParentTree 8 0 R
+ /ParentTreeNextKey 1
+>>
+endobj
+{{object 8 0}} <<
+ /Type /ParentTree
+ /Nums [0 [9 0 R 10 0 R 11 0 R 12 0 R]]
+>>
+endobj
+{{object 9 0}} <<
+ /Type /StructElem
+ /S /NonStruct
+ /P 7 0 R
+ /K 0
+ /ID /3
+>>
+endobj
+{{object 10 0}} <<
+ /Type /StructElem
+ /S /NonStruct
+ /P 7 0 R
+ /K <<
+ /Type /MCR
+ /MCID 1
+ /Pg 3 0 R
+ >>
+ /ID /4
+>>
+endobj
+{{object 11 0}} <<
+ /Type /StructElem
+ /S /NonStruct
+ /P 7 0 R
+ /K [
+ <<
+ /Type /MCR
+ /MCID 2
+ /Pg 3 0 R
+ >>
+ 3]
+ /ID /5
+>>
+endobj
+{{object 12 0}} <<
+ /Type /StructElem
+ /S /NonStruct
+ /P 7 0 R
+ /ID /6
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/tagged_marked_content.pdf b/testing/resources/tagged_marked_content.pdf
new file mode 100644
index 0000000..92f731d
--- /dev/null
+++ b/testing/resources/tagged_marked_content.pdf
@@ -0,0 +1,159 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /StructTreeRoot 7 0 R
+ /MarkInfo <<
+ /Type /MarkInfo
+ /Marked true
+ >>
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /StructParents 0
+ /Annots [4 0 R]
+ /Contents 5 0 R
+ /MediaBox [0 0 612 792]
+ /Resources <<
+ /ProcSet [/PDF /Text]
+ /Font <<
+ /F4 6 0 R
+ >>
+ >>
+>>
+endobj
+4 0 obj <<
+ /Type /Annot
+ /Subtype /Link
+ /Border [0 0 0]
+ /Dest /top
+ /F 4
+ /Rect [20 46 68 61]
+>>
+endobj
+5 0 obj <<
+ /Length 264
+>>
+stream
+q
+BT
+/P <</MCID 0 >>BDC
+/F4 16 Tf
+20 650 Td
+(Top Left) Tj
+EMC
+ET
+BT
+/P <</MCID 1 >>BDC
+/F4 16 Tf
+20 50 Td
+(Bottom Left) Tj
+EMC
+ET
+BT
+/P <</MCID 2 >>BDC
+/F4 16 Tf
+400 50 Td
+(Bottom Right) Tj
+EMC
+ET
+BT
+/P <</MCID 3 >>BDC
+/F4 16 Tf
+400 650 Td
+(Top Right) Tj
+EMC
+ET
+Q
+endstream
+endobj
+6 0 obj <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Times-Roman
+>>
+endobj
+7 0 obj <<
+ /Type /StructTreeRoot
+ /K [9 0 R 10 0 R 11 0 R 12 0 R]
+ /ParentTree 8 0 R
+ /ParentTreeNextKey 1
+>>
+endobj
+8 0 obj <<
+ /Type /ParentTree
+ /Nums [0 [9 0 R 10 0 R 11 0 R 12 0 R]]
+>>
+endobj
+9 0 obj <<
+ /Type /StructElem
+ /S /NonStruct
+ /P 7 0 R
+ /K 0
+ /ID /3
+>>
+endobj
+10 0 obj <<
+ /Type /StructElem
+ /S /NonStruct
+ /P 7 0 R
+ /K <<
+ /Type /MCR
+ /MCID 1
+ /Pg 3 0 R
+ >>
+ /ID /4
+>>
+endobj
+11 0 obj <<
+ /Type /StructElem
+ /S /NonStruct
+ /P 7 0 R
+ /K [
+ <<
+ /Type /MCR
+ /MCID 2
+ /Pg 3 0 R
+ >>
+ 3]
+ /ID /5
+>>
+endobj
+12 0 obj <<
+ /Type /StructElem
+ /S /NonStruct
+ /P 7 0 R
+ /ID /6
+>>
+endobj
+xref
+0 13
+0000000000 65535 f
+0000000015 00000 n
+0000000149 00000 n
+0000000212 00000 n
+0000000427 00000 n
+0000000540 00000 n
+0000000856 00000 n
+0000000934 00000 n
+0000001056 00000 n
+0000001138 00000 n
+0000001222 00000 n
+0000001363 00000 n
+0000001525 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 13
+>>
+startxref
+1603
+%%EOF
diff --git a/testing/resources/tagged_mcr_objr.in b/testing/resources/tagged_mcr_objr.in
new file mode 100644
index 0000000..3394633
--- /dev/null
+++ b/testing/resources/tagged_mcr_objr.in
@@ -0,0 +1,160 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /StructTreeRoot 7 0 R
+ /MarkInfo <<
+ /Type /MarkInfo
+ /Marked true
+ >>
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /StructParents 0
+ /Annots [4 0 R]
+ /Contents 5 0 R
+ /MediaBox [0 0 612 792]
+ /Resources <<
+ /ProcSet [/PDF /Text]
+ /Font <<
+ /F4 6 0 R
+ >>
+ >>
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Annot
+ /Subtype /Link
+ /Border [0 0 0]
+ /Dest /top
+ /F 4
+ /Rect [20 46 68 61]
+>>
+endobj
+{{object 5 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+BT
+/P <</MCID 0 >>BDC
+/F4 16 Tf
+20 650 Td
+(Hello, world!) Tj
+EMC
+ET
+BT
+/P <</MCID 1 >>BDC
+/F4 16 Tf
+20 50 Td
+(Link to top) Tj
+EMC
+ET
+Q
+endstream
+endobj
+{{object 6 0}} <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Times-Roman
+>>
+endobj
+{{object 7 0}} <<
+ /Type /StructTreeRoot
+ /K 8 0 R
+ /ParentTree 9 0 R
+ /ParentTreeNextKey 1
+>>
+endobj
+{{object 8 0}} <<
+ /Type /StructElem
+ /S /Document
+ /P 7 0 R
+ /K [10 0 R 11 0 R]
+ /ID /2
+ /Lang (en-US)
+>>
+endobj
+{{object 9 0}} <<
+ /Type /ParentTree
+ /Nums [0 [13 0 R 15 0 R]]
+>>
+endobj
+{{object 10 0}} <<
+ /Type /StructElem
+ /S /NonStruct
+ /P 8 0 R
+ /K [12 0 R]
+ /ID /6
+>>
+endobj
+{{object 11 0}} <<
+ /Type /StructElem
+ /S /P
+ /P 8 0 R
+ /K [14 0 R]
+ /ID /4
+>>
+endobj
+{{object 12 0}} <<
+ /Type /StructElem
+ /S /P
+ /P 10 0 R
+ /K [13 0 R]
+ /ID /3
+>>
+endobj
+{{object 13 0}} <<
+ /Type /StructElem
+ /S /NonStruct
+ /P 12 0 R
+ /K [
+ <<
+ /Type /MCR
+ /MCID 0
+ /Pg 3 0 R
+ >>
+ ]
+ /ID /7
+>>
+endobj
+{{object 14 0}} <<
+ /Type /StructElem
+ /S /NonStruct
+ /P 11 0 R
+ /K [
+ 15 0 R
+ <<
+ /Type /OBJR
+ /Obj 4 0 R
+ >>
+ ]
+ /ID /9
+>>
+endobj
+{{object 15 0}} <<
+ /Type /StructElem
+ /S /NonStruct
+ /P 14 0 R
+ /K [
+ <<
+ /Type /MCR
+ /Pg 3 0 R
+ /MCID 1
+ >>
+ ]
+ /ID /10
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/tagged_mcr_objr.pdf b/testing/resources/tagged_mcr_objr.pdf
new file mode 100644
index 0000000..a86faa7
--- /dev/null
+++ b/testing/resources/tagged_mcr_objr.pdf
@@ -0,0 +1,182 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /StructTreeRoot 7 0 R
+ /MarkInfo <<
+ /Type /MarkInfo
+ /Marked true
+ >>
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /StructParents 0
+ /Annots [4 0 R]
+ /Contents 5 0 R
+ /MediaBox [0 0 612 792]
+ /Resources <<
+ /ProcSet [/PDF /Text]
+ /Font <<
+ /F4 6 0 R
+ >>
+ >>
+>>
+endobj
+4 0 obj <<
+ /Type /Annot
+ /Subtype /Link
+ /Border [0 0 0]
+ /Dest /top
+ /F 4
+ /Rect [20 46 68 61]
+>>
+endobj
+5 0 obj <<
+ /Length 137
+>>
+stream
+q
+BT
+/P <</MCID 0 >>BDC
+/F4 16 Tf
+20 650 Td
+(Hello, world!) Tj
+EMC
+ET
+BT
+/P <</MCID 1 >>BDC
+/F4 16 Tf
+20 50 Td
+(Link to top) Tj
+EMC
+ET
+Q
+endstream
+endobj
+6 0 obj <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Times-Roman
+>>
+endobj
+7 0 obj <<
+ /Type /StructTreeRoot
+ /K 8 0 R
+ /ParentTree 9 0 R
+ /ParentTreeNextKey 1
+>>
+endobj
+8 0 obj <<
+ /Type /StructElem
+ /S /Document
+ /P 7 0 R
+ /K [10 0 R 11 0 R]
+ /ID /2
+ /Lang (en-US)
+>>
+endobj
+9 0 obj <<
+ /Type /ParentTree
+ /Nums [0 [13 0 R 15 0 R]]
+>>
+endobj
+10 0 obj <<
+ /Type /StructElem
+ /S /NonStruct
+ /P 8 0 R
+ /K [12 0 R]
+ /ID /6
+>>
+endobj
+11 0 obj <<
+ /Type /StructElem
+ /S /P
+ /P 8 0 R
+ /K [14 0 R]
+ /ID /4
+>>
+endobj
+12 0 obj <<
+ /Type /StructElem
+ /S /P
+ /P 10 0 R
+ /K [13 0 R]
+ /ID /3
+>>
+endobj
+13 0 obj <<
+ /Type /StructElem
+ /S /NonStruct
+ /P 12 0 R
+ /K [
+ <<
+ /Type /MCR
+ /MCID 0
+ /Pg 3 0 R
+ >>
+ ]
+ /ID /7
+>>
+endobj
+14 0 obj <<
+ /Type /StructElem
+ /S /NonStruct
+ /P 11 0 R
+ /K [
+ 15 0 R
+ <<
+ /Type /OBJR
+ /Obj 4 0 R
+ >>
+ ]
+ /ID /9
+>>
+endobj
+15 0 obj <<
+ /Type /StructElem
+ /S /NonStruct
+ /P 14 0 R
+ /K [
+ <<
+ /Type /MCR
+ /Pg 3 0 R
+ /MCID 1
+ >>
+ ]
+ /ID /10
+>>
+endobj
+xref
+0 16
+0000000000 65535 f
+0000000015 00000 n
+0000000149 00000 n
+0000000212 00000 n
+0000000427 00000 n
+0000000540 00000 n
+0000000729 00000 n
+0000000807 00000 n
+0000000906 00000 n
+0000001019 00000 n
+0000001088 00000 n
+0000001180 00000 n
+0000001264 00000 n
+0000001349 00000 n
+0000001500 00000 n
+0000001650 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 16
+>>
+startxref
+1802
+%%EOF
diff --git a/testing/resources/tagged_nested.in b/testing/resources/tagged_nested.in
new file mode 100644
index 0000000..b7d2022
--- /dev/null
+++ b/testing/resources/tagged_nested.in
@@ -0,0 +1,346 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /StructTreeRoot 5 0 R
+ /Lang (en-US)
+ /MarkInfo <<
+ /Marked true
+ >>
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /MediaBox [0 0 612 792]
+ /StructParents 0
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+BT
+/Para<</MCID 0>>
+BMC
+0 0 Td
+/F1 18 Tf
+(Sample Text) Tj
+EMC
+ET
+endstream
+endobj
+{{object 5 0}} <<
+ /Type /StructTreeRoot
+ /ParentTree 6 0 R
+ /K [8 0 R]
+ /RoleMap <<
+ /Document /Document
+ /Para /Para
+ >>
+>>
+endobj
+{{object 6 0}} <<
+ /Nums [0 [7 0 R]]
+>>
+endobj
+{{object 7 0}} <<
+ /Type /StructElem
+ /S /Para
+ /K [0]
+ /P 42 0 R
+ /Pg 3 0 R
+>>
+endobj
+{{object 8 0}} <<
+ /Type /StructElem
+ /S /Document
+ /K [9 0 R]
+ /P 5 0 R
+ /T (TitleText)
+ /Pg 3 0 R
+>>
+endobj
+{{object 9 0}} <<
+ /Type /StructElem
+ /S /Para
+ /K [10 0 R]
+ /P 8 0 R
+ /Pg 3 0 R
+>>
+endobj
+{{object 10 0}} <<
+ /Type /StructElem
+ /S /Para
+ /K [11 0 R]
+ /P 9 0 R
+ /Pg 3 0 R
+>>
+endobj
+{{object 11 0}} <<
+ /Type /StructElem
+ /S /Para
+ /K [12 0 R]
+ /P 10 0 R
+ /Pg 3 0 R
+>>
+endobj
+{{object 12 0}} <<
+ /Type /StructElem
+ /S /Para
+ /K [13 0 R]
+ /P 11 0 R
+ /Pg 3 0 R
+>>
+endobj
+{{object 13 0}} <<
+ /Type /StructElem
+ /S /Para
+ /K [14 0 R]
+ /P 12 0 R
+ /Pg 3 0 R
+>>
+endobj
+{{object 14 0}} <<
+ /Type /StructElem
+ /S /Para
+ /K [15 0 R]
+ /P 13 0 R
+ /Pg 3 0 R
+>>
+endobj
+{{object 15 0}} <<
+ /Type /StructElem
+ /S /Para
+ /K [16 0 R]
+ /P 14 0 R
+ /Pg 3 0 R
+>>
+endobj
+{{object 16 0}} <<
+ /Type /StructElem
+ /S /Para
+ /K [17 0 R]
+ /P 15 0 R
+ /Pg 3 0 R
+>>
+endobj
+{{object 17 0}} <<
+ /Type /StructElem
+ /S /Para
+ /K [18 0 R]
+ /P 16 0 R
+ /Pg 3 0 R
+>>
+endobj
+{{object 18 0}} <<
+ /Type /StructElem
+ /S /Para
+ /K [19 0 R]
+ /P 17 0 R
+ /Pg 3 0 R
+>>
+endobj
+{{object 19 0}} <<
+ /Type /StructElem
+ /S /Para
+ /K [20 0 R]
+ /P 18 0 R
+ /Pg 3 0 R
+>>
+endobj
+{{object 20 0}} <<
+ /Type /StructElem
+ /S /Para
+ /K [21 0 R]
+ /P 19 0 R
+ /Pg 3 0 R
+>>
+endobj
+{{object 21 0}} <<
+ /Type /StructElem
+ /S /Para
+ /K [22 0 R]
+ /P 20 0 R
+ /Pg 3 0 R
+>>
+endobj
+{{object 22 0}} <<
+ /Type /StructElem
+ /S /Para
+ /K [23 0 R]
+ /P 21 0 R
+ /Pg 3 0 R
+>>
+endobj
+{{object 23 0}} <<
+ /Type /StructElem
+ /S /Para
+ /K [24 0 R]
+ /P 22 0 R
+ /Pg 3 0 R
+>>
+endobj
+{{object 24 0}} <<
+ /Type /StructElem
+ /S /Para
+ /K [25 0 R]
+ /P 23 0 R
+ /Pg 3 0 R
+>>
+endobj
+{{object 25 0}} <<
+ /Type /StructElem
+ /S /Para
+ /K [26 0 R]
+ /P 24 0 R
+ /Pg 3 0 R
+>>
+endobj
+{{object 26 0}} <<
+ /Type /StructElem
+ /S /Para
+ /K [27 0 R]
+ /P 25 0 R
+ /Pg 3 0 R
+>>
+endobj
+{{object 27 0}} <<
+ /Type /StructElem
+ /S /Para
+ /K [28 0 R]
+ /P 26 0 R
+ /Pg 3 0 R
+>>
+endobj
+{{object 28 0}} <<
+ /Type /StructElem
+ /S /Para
+ /K [29 0 R]
+ /P 27 0 R
+ /Pg 3 0 R
+>>
+endobj
+{{object 29 0}} <<
+ /Type /StructElem
+ /S /Para
+ /K [30 0 R]
+ /P 28 0 R
+ /Pg 3 0 R
+>>
+endobj
+{{object 30 0}} <<
+ /Type /StructElem
+ /S /Para
+ /K [31 0 R]
+ /P 29 0 R
+ /Pg 3 0 R
+>>
+endobj
+{{object 31 0}} <<
+ /Type /StructElem
+ /S /Para
+ /K [32 0 R]
+ /P 30 0 R
+ /Pg 3 0 R
+>>
+endobj
+{{object 32 0}} <<
+ /Type /StructElem
+ /S /Para
+ /K [33 0 R]
+ /P 31 0 R
+ /Pg 3 0 R
+>>
+endobj
+{{object 33 0}} <<
+ /Type /StructElem
+ /S /Para
+ /K [34 0 R]
+ /P 32 0 R
+ /Pg 3 0 R
+>>
+endobj
+{{object 34 0}} <<
+ /Type /StructElem
+ /S /Para
+ /K [35 0 R]
+ /P 33 0 R
+ /Pg 3 0 R
+>>
+endobj
+{{object 35 0}} <<
+ /Type /StructElem
+ /S /Para
+ /K [36 0 R]
+ /P 34 0 R
+ /Pg 3 0 R
+>>
+endobj
+{{object 36 0}} <<
+ /Type /StructElem
+ /S /Para
+ /K [37 0 R]
+ /P 35 0 R
+ /Pg 3 0 R
+>>
+endobj
+{{object 37 0}} <<
+ /Type /StructElem
+ /S /Para
+ /K [38 0 R]
+ /P 36 0 R
+ /Pg 3 0 R
+>>
+endobj
+{{object 38 0}} <<
+ /Type /StructElem
+ /S /Para
+ /K [39 0 R]
+ /P 37 0 R
+ /Pg 3 0 R
+>>
+endobj
+{{object 39 0}} <<
+ /Type /StructElem
+ /S /Para
+ /K [40 0 R]
+ /P 38 0 R
+ /Pg 3 0 R
+>>
+endobj
+{{object 40 0}} <<
+ /Type /StructElem
+ /S /Para
+ /K [41 0 R]
+ /P 39 0 R
+ /Pg 3 0 R
+>>
+endobj
+{{object 41 0}} <<
+ /Type /StructElem
+ /S /Para
+ /K [42 0 R]
+ /P 40 0 R
+ /Pg 3 0 R
+>>
+endobj
+{{object 42 0}} <<
+ /Type /StructElem
+ /S /Para
+ /K [7 0 R]
+ /P 41 0 R
+ /Pg 3 0 R
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/tagged_nested.pdf b/testing/resources/tagged_nested.pdf
new file mode 100644
index 0000000..8e09360
--- /dev/null
+++ b/testing/resources/tagged_nested.pdf
@@ -0,0 +1,395 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /StructTreeRoot 5 0 R
+ /Lang (en-US)
+ /MarkInfo <<
+ /Marked true
+ >>
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /MediaBox [0 0 612 792]
+ /StructParents 0
+>>
+endobj
+4 0 obj <<
+ /Length 65
+>>
+stream
+BT
+/Para<</MCID 0>>
+BMC
+0 0 Td
+/F1 18 Tf
+(Sample Text) Tj
+EMC
+ET
+endstream
+endobj
+5 0 obj <<
+ /Type /StructTreeRoot
+ /ParentTree 6 0 R
+ /K [8 0 R]
+ /RoleMap <<
+ /Document /Document
+ /Para /Para
+ >>
+>>
+endobj
+6 0 obj <<
+ /Nums [0 [7 0 R]]
+>>
+endobj
+7 0 obj <<
+ /Type /StructElem
+ /S /Para
+ /K [0]
+ /P 42 0 R
+ /Pg 3 0 R
+>>
+endobj
+8 0 obj <<
+ /Type /StructElem
+ /S /Document
+ /K [9 0 R]
+ /P 5 0 R
+ /T (TitleText)
+ /Pg 3 0 R
+>>
+endobj
+9 0 obj <<
+ /Type /StructElem
+ /S /Para
+ /K [10 0 R]
+ /P 8 0 R
+ /Pg 3 0 R
+>>
+endobj
+10 0 obj <<
+ /Type /StructElem
+ /S /Para
+ /K [11 0 R]
+ /P 9 0 R
+ /Pg 3 0 R
+>>
+endobj
+11 0 obj <<
+ /Type /StructElem
+ /S /Para
+ /K [12 0 R]
+ /P 10 0 R
+ /Pg 3 0 R
+>>
+endobj
+12 0 obj <<
+ /Type /StructElem
+ /S /Para
+ /K [13 0 R]
+ /P 11 0 R
+ /Pg 3 0 R
+>>
+endobj
+13 0 obj <<
+ /Type /StructElem
+ /S /Para
+ /K [14 0 R]
+ /P 12 0 R
+ /Pg 3 0 R
+>>
+endobj
+14 0 obj <<
+ /Type /StructElem
+ /S /Para
+ /K [15 0 R]
+ /P 13 0 R
+ /Pg 3 0 R
+>>
+endobj
+15 0 obj <<
+ /Type /StructElem
+ /S /Para
+ /K [16 0 R]
+ /P 14 0 R
+ /Pg 3 0 R
+>>
+endobj
+16 0 obj <<
+ /Type /StructElem
+ /S /Para
+ /K [17 0 R]
+ /P 15 0 R
+ /Pg 3 0 R
+>>
+endobj
+17 0 obj <<
+ /Type /StructElem
+ /S /Para
+ /K [18 0 R]
+ /P 16 0 R
+ /Pg 3 0 R
+>>
+endobj
+18 0 obj <<
+ /Type /StructElem
+ /S /Para
+ /K [19 0 R]
+ /P 17 0 R
+ /Pg 3 0 R
+>>
+endobj
+19 0 obj <<
+ /Type /StructElem
+ /S /Para
+ /K [20 0 R]
+ /P 18 0 R
+ /Pg 3 0 R
+>>
+endobj
+20 0 obj <<
+ /Type /StructElem
+ /S /Para
+ /K [21 0 R]
+ /P 19 0 R
+ /Pg 3 0 R
+>>
+endobj
+21 0 obj <<
+ /Type /StructElem
+ /S /Para
+ /K [22 0 R]
+ /P 20 0 R
+ /Pg 3 0 R
+>>
+endobj
+22 0 obj <<
+ /Type /StructElem
+ /S /Para
+ /K [23 0 R]
+ /P 21 0 R
+ /Pg 3 0 R
+>>
+endobj
+23 0 obj <<
+ /Type /StructElem
+ /S /Para
+ /K [24 0 R]
+ /P 22 0 R
+ /Pg 3 0 R
+>>
+endobj
+24 0 obj <<
+ /Type /StructElem
+ /S /Para
+ /K [25 0 R]
+ /P 23 0 R
+ /Pg 3 0 R
+>>
+endobj
+25 0 obj <<
+ /Type /StructElem
+ /S /Para
+ /K [26 0 R]
+ /P 24 0 R
+ /Pg 3 0 R
+>>
+endobj
+26 0 obj <<
+ /Type /StructElem
+ /S /Para
+ /K [27 0 R]
+ /P 25 0 R
+ /Pg 3 0 R
+>>
+endobj
+27 0 obj <<
+ /Type /StructElem
+ /S /Para
+ /K [28 0 R]
+ /P 26 0 R
+ /Pg 3 0 R
+>>
+endobj
+28 0 obj <<
+ /Type /StructElem
+ /S /Para
+ /K [29 0 R]
+ /P 27 0 R
+ /Pg 3 0 R
+>>
+endobj
+29 0 obj <<
+ /Type /StructElem
+ /S /Para
+ /K [30 0 R]
+ /P 28 0 R
+ /Pg 3 0 R
+>>
+endobj
+30 0 obj <<
+ /Type /StructElem
+ /S /Para
+ /K [31 0 R]
+ /P 29 0 R
+ /Pg 3 0 R
+>>
+endobj
+31 0 obj <<
+ /Type /StructElem
+ /S /Para
+ /K [32 0 R]
+ /P 30 0 R
+ /Pg 3 0 R
+>>
+endobj
+32 0 obj <<
+ /Type /StructElem
+ /S /Para
+ /K [33 0 R]
+ /P 31 0 R
+ /Pg 3 0 R
+>>
+endobj
+33 0 obj <<
+ /Type /StructElem
+ /S /Para
+ /K [34 0 R]
+ /P 32 0 R
+ /Pg 3 0 R
+>>
+endobj
+34 0 obj <<
+ /Type /StructElem
+ /S /Para
+ /K [35 0 R]
+ /P 33 0 R
+ /Pg 3 0 R
+>>
+endobj
+35 0 obj <<
+ /Type /StructElem
+ /S /Para
+ /K [36 0 R]
+ /P 34 0 R
+ /Pg 3 0 R
+>>
+endobj
+36 0 obj <<
+ /Type /StructElem
+ /S /Para
+ /K [37 0 R]
+ /P 35 0 R
+ /Pg 3 0 R
+>>
+endobj
+37 0 obj <<
+ /Type /StructElem
+ /S /Para
+ /K [38 0 R]
+ /P 36 0 R
+ /Pg 3 0 R
+>>
+endobj
+38 0 obj <<
+ /Type /StructElem
+ /S /Para
+ /K [39 0 R]
+ /P 37 0 R
+ /Pg 3 0 R
+>>
+endobj
+39 0 obj <<
+ /Type /StructElem
+ /S /Para
+ /K [40 0 R]
+ /P 38 0 R
+ /Pg 3 0 R
+>>
+endobj
+40 0 obj <<
+ /Type /StructElem
+ /S /Para
+ /K [41 0 R]
+ /P 39 0 R
+ /Pg 3 0 R
+>>
+endobj
+41 0 obj <<
+ /Type /StructElem
+ /S /Para
+ /K [42 0 R]
+ /P 40 0 R
+ /Pg 3 0 R
+>>
+endobj
+42 0 obj <<
+ /Type /StructElem
+ /S /Para
+ /K [7 0 R]
+ /P 41 0 R
+ /Pg 3 0 R
+>>
+endobj
+xref
+0 43
+0000000000 65535 f
+0000000015 00000 n
+0000000145 00000 n
+0000000208 00000 n
+0000000322 00000 n
+0000000438 00000 n
+0000000575 00000 n
+0000000616 00000 n
+0000000701 00000 n
+0000000810 00000 n
+0000000899 00000 n
+0000000989 00000 n
+0000001080 00000 n
+0000001171 00000 n
+0000001262 00000 n
+0000001353 00000 n
+0000001444 00000 n
+0000001535 00000 n
+0000001626 00000 n
+0000001717 00000 n
+0000001808 00000 n
+0000001899 00000 n
+0000001990 00000 n
+0000002081 00000 n
+0000002172 00000 n
+0000002263 00000 n
+0000002354 00000 n
+0000002445 00000 n
+0000002536 00000 n
+0000002627 00000 n
+0000002718 00000 n
+0000002809 00000 n
+0000002900 00000 n
+0000002991 00000 n
+0000003082 00000 n
+0000003173 00000 n
+0000003264 00000 n
+0000003355 00000 n
+0000003446 00000 n
+0000003537 00000 n
+0000003628 00000 n
+0000003719 00000 n
+0000003810 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 43
+>>
+startxref
+3900
+%%EOF
diff --git a/testing/resources/tagged_table.in b/testing/resources/tagged_table.in
new file mode 100644
index 0000000..ee298c5
--- /dev/null
+++ b/testing/resources/tagged_table.in
@@ -0,0 +1,217 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /StructTreeRoot 8 0 R
+ /Lang (en-US)
+ /MarkInfo <<
+ /Marked true
+ >>
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /MediaBox [0 0 612 792]
+ /Group <<
+ /CS /DeviceRGB
+ /I true
+ /S /Transparency
+ >>
+ /Resources <<
+ /ProcSet [/PDF /ImageC /ImageI /ImageB]
+ /XObject <<
+ /Tr8 5 0 R
+ /Im7 6 0 R
+ >>
+ /ExtGState <<
+ /EGS9 7 0 R
+ >>
+ >>
+ /StructParents 0
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+0.1 w
+/Artifact
+BMC
+q
+0 0 612 792 re
+W* n
+EMC
+/Figure<</MCID 0>>
+BDC
+Q
+q
+281 685.3 50 50 re
+W* n
+q
+49.9 0 0 50 281.1 685.4 cm
+/Im7 Do
+Q
+EMC
+Q
+q
+EGS9 gs /Tr8 Do
+Q
+endstream
+endobj
+{{object 5 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /BBox [-140 395 753 395.1]
+ /Group <<
+ /CS /DeviceRGB
+ /K true
+ /S /Transparency
+ >>
+ {{streamlen}}
+>>
+stream
+endstream
+endobj
+{{object 6 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /Width 50
+ /Height 50
+ /BitsPerComponent 8
+ /ColorSpace /DeviceRGB
+ /Filter [/ASCIIHexDecode /FlateDecode]
+ {{streamlen}}
+>>
+stream
+789cedc13101000000c2a0f54fed6f06a00000000000000078031d4c0001
+endstream
+endobj
+{{object 7 0}} <<
+ /ca 0.5
+ /CA 0.5
+>>
+endobj
+{{object 8 0}} <<
+ /Type /StructTreeRoot
+ /ParentTree 9 0 R
+ /K [10 0 R]
+ /RoleMap <<
+ /Document /Document
+ /Standard /P
+ /Figure /Figure
+ >>
+>>
+endobj
+{{object 9 0}} <<
+ /Nums [
+ 0
+ [10 0 R 11 0 R 12 0 R 13 0 R 14 0 R 15 0 R 16 0 R 17 0 R]
+ ]
+>>
+endobj
+{{object 10 0}} <<
+ /Type /StructElem
+ /S /Document
+ /K [11 0 R]
+ /P 8 0 R
+ /T (TitleText)
+ /Pg 3 0 R
+ /Lang (en-US)
+>>
+endobj
+{{object 11 0}} <<
+ /Type /StructElem
+ /S /Table
+ /K [12 0 R 13 0 R]
+ /P 10 0 R
+ /Pg 3 0 R
+ /A [<<
+ /O /Table
+ /Summary ()
+ >>]
+ /ID (node12)
+ /Lang (hu)
+>>
+endobj
+{{object 12 0}} <<
+ /Type /StructElem
+ /S /TR
+ /K [14 0 R 15 0 R]
+ /P 11 0 R
+ /Pg 3 0 R
+ /ID ()
+>>
+endobj
+{{object 13 0}} <<
+ /Type /StructElem
+ /S /TR
+ /K [16 0 R 17 0 R]
+ /P 11 0 R
+ /Pg 3 0 R
+ /A <<
+ /O /Table
+ >>
+ /ID (node14)
+>>
+endobj
+{{object 14 0}} <<
+ /Type /StructElem
+ /S /TH
+ /P 12 0 R
+ /Pg 3 0 R
+ /A [<<
+ /O /Table
+ /Scope /Row
+ >>
+ <<
+ /O /Table
+ /ColSpan 2
+ >>]
+ /ID (node15)
+>>
+endobj
+{{object 15 0}} <<
+ /Type /StructElem
+ /S /TD
+ /P 12 0 R
+ /Pg 3 0 R
+ /ID (node16)
+>>
+endobj
+{{object 16 0}} <<
+ /Type /StructElem
+ /S /TH
+ /P 13 0 R
+ /Pg 3 0 R
+ /A [<<
+ /O /Table
+ /Scope /Row
+ >>]
+ /ID (node17)
+>>
+endobj
+{{object 17 0}} <<
+ /Type /StructElem
+ /S /TD
+ /P 13 0 R
+ /Pg 3 0 R
+ /A [<<
+ /O /Table
+ /ColProp (Sum)
+ /CurUSD true
+ >>]
+ /ID (node18)
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/tagged_table.pdf b/testing/resources/tagged_table.pdf
new file mode 100644
index 0000000..4428682
--- /dev/null
+++ b/testing/resources/tagged_table.pdf
@@ -0,0 +1,241 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /StructTreeRoot 8 0 R
+ /Lang (en-US)
+ /MarkInfo <<
+ /Marked true
+ >>
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /MediaBox [0 0 612 792]
+ /Group <<
+ /CS /DeviceRGB
+ /I true
+ /S /Transparency
+ >>
+ /Resources <<
+ /ProcSet [/PDF /ImageC /ImageI /ImageB]
+ /XObject <<
+ /Tr8 5 0 R
+ /Im7 6 0 R
+ >>
+ /ExtGState <<
+ /EGS9 7 0 R
+ >>
+ >>
+ /StructParents 0
+>>
+endobj
+4 0 obj <<
+ /Length 162
+>>
+stream
+0.1 w
+/Artifact
+BMC
+q
+0 0 612 792 re
+W* n
+EMC
+/Figure<</MCID 0>>
+BDC
+Q
+q
+281 685.3 50 50 re
+W* n
+q
+49.9 0 0 50 281.1 685.4 cm
+/Im7 Do
+Q
+EMC
+Q
+q
+EGS9 gs /Tr8 Do
+Q
+endstream
+endobj
+5 0 obj <<
+ /Type /XObject
+ /Subtype /Form
+ /BBox [-140 395 753 395.1]
+ /Group <<
+ /CS /DeviceRGB
+ /K true
+ /S /Transparency
+ >>
+ /Length 0
+>>
+stream
+endstream
+endobj
+6 0 obj <<
+ /Type /XObject
+ /Subtype /Image
+ /Width 50
+ /Height 50
+ /BitsPerComponent 8
+ /ColorSpace /DeviceRGB
+ /Filter [/ASCIIHexDecode /FlateDecode]
+ /Length 61
+>>
+stream
+789cedc13101000000c2a0f54fed6f06a00000000000000078031d4c0001
+endstream
+endobj
+7 0 obj <<
+ /ca 0.5
+ /CA 0.5
+>>
+endobj
+8 0 obj <<
+ /Type /StructTreeRoot
+ /ParentTree 9 0 R
+ /K [10 0 R]
+ /RoleMap <<
+ /Document /Document
+ /Standard /P
+ /Figure /Figure
+ >>
+>>
+endobj
+9 0 obj <<
+ /Nums [
+ 0
+ [10 0 R 11 0 R 12 0 R 13 0 R 14 0 R 15 0 R 16 0 R 17 0 R]
+ ]
+>>
+endobj
+10 0 obj <<
+ /Type /StructElem
+ /S /Document
+ /K [11 0 R]
+ /P 8 0 R
+ /T (TitleText)
+ /Pg 3 0 R
+ /Lang (en-US)
+>>
+endobj
+11 0 obj <<
+ /Type /StructElem
+ /S /Table
+ /K [12 0 R 13 0 R]
+ /P 10 0 R
+ /Pg 3 0 R
+ /A [<<
+ /O /Table
+ /Summary ()
+ >>]
+ /ID (node12)
+ /Lang (hu)
+>>
+endobj
+12 0 obj <<
+ /Type /StructElem
+ /S /TR
+ /K [14 0 R 15 0 R]
+ /P 11 0 R
+ /Pg 3 0 R
+ /ID ()
+>>
+endobj
+13 0 obj <<
+ /Type /StructElem
+ /S /TR
+ /K [16 0 R 17 0 R]
+ /P 11 0 R
+ /Pg 3 0 R
+ /A <<
+ /O /Table
+ >>
+ /ID (node14)
+>>
+endobj
+14 0 obj <<
+ /Type /StructElem
+ /S /TH
+ /P 12 0 R
+ /Pg 3 0 R
+ /A [<<
+ /O /Table
+ /Scope /Row
+ >>
+ <<
+ /O /Table
+ /ColSpan 2
+ >>]
+ /ID (node15)
+>>
+endobj
+15 0 obj <<
+ /Type /StructElem
+ /S /TD
+ /P 12 0 R
+ /Pg 3 0 R
+ /ID (node16)
+>>
+endobj
+16 0 obj <<
+ /Type /StructElem
+ /S /TH
+ /P 13 0 R
+ /Pg 3 0 R
+ /A [<<
+ /O /Table
+ /Scope /Row
+ >>]
+ /ID (node17)
+>>
+endobj
+17 0 obj <<
+ /Type /StructElem
+ /S /TD
+ /P 13 0 R
+ /Pg 3 0 R
+ /A [<<
+ /O /Table
+ /ColProp (Sum)
+ /CurUSD true
+ >>]
+ /ID (node18)
+>>
+endobj
+xref
+0 18
+0000000000 65535 f
+0000000015 00000 n
+0000000145 00000 n
+0000000208 00000 n
+0000000556 00000 n
+0000000770 00000 n
+0000000952 00000 n
+0000001212 00000 n
+0000001253 00000 n
+0000001412 00000 n
+0000001515 00000 n
+0000001642 00000 n
+0000001826 00000 n
+0000001931 00000 n
+0000002074 00000 n
+0000002276 00000 n
+0000002366 00000 n
+0000002513 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 18
+>>
+startxref
+2683
+%%EOF
diff --git a/testing/resources/tagged_table_bad_elem.in b/testing/resources/tagged_table_bad_elem.in
new file mode 100644
index 0000000..c502047
--- /dev/null
+++ b/testing/resources/tagged_table_bad_elem.in
@@ -0,0 +1,153 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /StructTreeRoot 8 0 R
+ /Lang (en-US)
+ /MarkInfo <<
+ /Marked true
+ >>
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /MediaBox [0 0 612 792]
+ /Group <<
+ /CS /DeviceRGB
+ /I true
+ /S /Transparency
+ >>
+ /Resources <<
+ /ProcSet [/PDF /ImageC /ImageI /ImageB]
+ /XObject <<
+ /Tr8 5 0 R
+ /Im7 6 0 R
+ >>
+ /ExtGState <<
+ /EGS9 7 0 R
+ >>
+ >>
+ /StructParents 0
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+0.1 w
+/Artifact
+BMC
+q
+0 0 612 792 re
+W* n
+EMC
+/Figure<</MCID 0>>
+BDC
+Q
+q
+281 685.3 50 50 re
+W* n
+q
+49.9 0 0 50 281.1 685.4 cm
+/Im7 Do
+Q
+EMC
+Q
+q
+EGS9 gs /Tr8 Do
+Q
+endstream
+endobj
+{{object 5 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /BBox [-140 395 753 395.1]
+ /Group <<
+ /CS /DeviceRGB
+ /K true
+ /S /Transparency
+ >>
+ {{streamlen}}
+>>
+stream
+endstream
+endobj
+{{object 6 0}} <<
+ /Type /XObject
+ /Subtype /Image
+ /Width 50
+ /Height 50
+ /BitsPerComponent 8
+ /ColorSpace /DeviceRGB
+ /Filter [/ASCIIHexDecode /FlateDecode]
+ {{streamlen}}
+>>
+stream
+789cedc13101000000c2a0f54fed6f06a00000000000000078031d4c0001
+endstream
+endobj
+{{object 7 0}} <<
+ /ca 0.5
+ /CA 0.5
+>>
+endobj
+{{object 8 0}} <<
+ /Type /StructTreeRoot
+ /ParentTree 9 0 R
+ /K [10 0 R]
+ /RoleMap <<
+ /Document /Document
+ /Standard /P
+ /Figure /Figure
+ >>
+>>
+endobj
+{{object 9 0}} <<
+ /Nums [
+ 0
+ [10 0 R 11 0 R 12 0 R]
+ ]
+>>
+endobj
+{{object 10 0}} <<
+ /Type /StructElem
+ /S /Document
+ /K [11 0 R]
+ /P 8 0 R
+ /T (TitleText)
+ /Pg 3 0 R
+ /Lang (en-US)
+>>
+endobj
+{{object 11 0}} <<
+ % Deliberately missing /Type
+ /S /Table
+ /K [12 0 R]
+ /P 10 0 R
+ /Pg 3 0 R
+ /A [(bogus type)]
+ /ID (node12)
+ /Lang (hu)
+>>
+endobj
+{{object 12 0}} <<
+ % Deliberately bad /Type.
+ /Type /NotStructElem
+ /S /TR
+ /P 11 0 R
+ /Pg 3 0 R
+ /ID ()
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/tagged_table_bad_elem.pdf b/testing/resources/tagged_table_bad_elem.pdf
new file mode 100644
index 0000000..62beee0
--- /dev/null
+++ b/testing/resources/tagged_table_bad_elem.pdf
@@ -0,0 +1,172 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /StructTreeRoot 8 0 R
+ /Lang (en-US)
+ /MarkInfo <<
+ /Marked true
+ >>
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /MediaBox [0 0 612 792]
+ /Group <<
+ /CS /DeviceRGB
+ /I true
+ /S /Transparency
+ >>
+ /Resources <<
+ /ProcSet [/PDF /ImageC /ImageI /ImageB]
+ /XObject <<
+ /Tr8 5 0 R
+ /Im7 6 0 R
+ >>
+ /ExtGState <<
+ /EGS9 7 0 R
+ >>
+ >>
+ /StructParents 0
+>>
+endobj
+4 0 obj <<
+ /Length 162
+>>
+stream
+0.1 w
+/Artifact
+BMC
+q
+0 0 612 792 re
+W* n
+EMC
+/Figure<</MCID 0>>
+BDC
+Q
+q
+281 685.3 50 50 re
+W* n
+q
+49.9 0 0 50 281.1 685.4 cm
+/Im7 Do
+Q
+EMC
+Q
+q
+EGS9 gs /Tr8 Do
+Q
+endstream
+endobj
+5 0 obj <<
+ /Type /XObject
+ /Subtype /Form
+ /BBox [-140 395 753 395.1]
+ /Group <<
+ /CS /DeviceRGB
+ /K true
+ /S /Transparency
+ >>
+ /Length 0
+>>
+stream
+endstream
+endobj
+6 0 obj <<
+ /Type /XObject
+ /Subtype /Image
+ /Width 50
+ /Height 50
+ /BitsPerComponent 8
+ /ColorSpace /DeviceRGB
+ /Filter [/ASCIIHexDecode /FlateDecode]
+ /Length 61
+>>
+stream
+789cedc13101000000c2a0f54fed6f06a00000000000000078031d4c0001
+endstream
+endobj
+7 0 obj <<
+ /ca 0.5
+ /CA 0.5
+>>
+endobj
+8 0 obj <<
+ /Type /StructTreeRoot
+ /ParentTree 9 0 R
+ /K [10 0 R]
+ /RoleMap <<
+ /Document /Document
+ /Standard /P
+ /Figure /Figure
+ >>
+>>
+endobj
+9 0 obj <<
+ /Nums [
+ 0
+ [10 0 R 11 0 R 12 0 R]
+ ]
+>>
+endobj
+10 0 obj <<
+ /Type /StructElem
+ /S /Document
+ /K [11 0 R]
+ /P 8 0 R
+ /T (TitleText)
+ /Pg 3 0 R
+ /Lang (en-US)
+>>
+endobj
+11 0 obj <<
+ % Deliberately missing /Type
+ /S /Table
+ /K [12 0 R]
+ /P 10 0 R
+ /Pg 3 0 R
+ /A [(bogus type)]
+ /ID (node12)
+ /Lang (hu)
+>>
+endobj
+12 0 obj <<
+ % Deliberately bad /Type.
+ /Type /NotStructElem
+ /S /TR
+ /P 11 0 R
+ /Pg 3 0 R
+ /ID ()
+>>
+endobj
+xref
+0 13
+0000000000 65535 f
+0000000015 00000 n
+0000000145 00000 n
+0000000208 00000 n
+0000000556 00000 n
+0000000770 00000 n
+0000000952 00000 n
+0000001212 00000 n
+0000001253 00000 n
+0000001412 00000 n
+0000001480 00000 n
+0000001607 00000 n
+0000001758 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 13
+>>
+startxref
+1873
+%%EOF
diff --git a/testing/resources/text_in_page_marked.in b/testing/resources/text_in_page_marked.in
index e338479..5978110 100644
--- a/testing/resources/text_in_page_marked.in
+++ b/testing/resources/text_in_page_marked.in
@@ -131,7 +131,7 @@
{{xref}}
trailer <<
/Root 1 0 R
- /Size 13
+ {{trailersize}}
/ID [<f341ae654a77acd5065a7645e596e6e6><bc37298a3f87f479229bce997ca791f7>]
>>
{{startxref}}
diff --git a/testing/resources/text_in_page_marked_indirect.in b/testing/resources/text_in_page_marked_indirect.in
index 1f989f6..6004ad4 100644
--- a/testing/resources/text_in_page_marked_indirect.in
+++ b/testing/resources/text_in_page_marked_indirect.in
@@ -137,7 +137,7 @@
{{xref}}
trailer <<
/Root 1 0 R
- /Size 14
+ {{trailersize}}
/ID [<f341ae654a77acd5065a7645e596e6e6><bc37298a3f87f479229bce997ca791f7>]
>>
{{startxref}}
diff --git a/testing/resources/trailer_end_trailing_space.in b/testing/resources/trailer_end_trailing_space.in
new file mode 100644
index 0000000..0233b75
--- /dev/null
+++ b/testing/resources/trailer_end_trailing_space.in
@@ -0,0 +1,86 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /MediaBox [0 0 200 200]
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Resources <<
+ /Font <<
+ /F1 4 0 R
+ /F2 5 0 R
+ >>
+ >>
+ /Contents 6 0 R
+>>
+endobj
+{{object 4 0}} <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Times-Roman
+>>
+endobj
+% Single space after endstream and endobj.
+{{object 6 0}} <<
+ {{streamlen}}
+>>
+stream
+BT
+20 50 Td
+/F1 12 Tf
+(Hello, world!) Tj
+0 50 Td
+/F2 16 Tf
+(Goodbye, world!) Tj
+ET
+endstream
+endobj
+% Multiple spaces after endstream and endobj.
+{{object 6 0}} <<
+ {{streamlen}}
+>>
+stream
+BT
+20 50 Td
+/F1 12 Tf
+(Hello, world!) Tj
+0 50 Td
+/F2 16 Tf
+(Goodbye, world!) Tj
+ET
+endstream
+endobj
+% Tab after endstream and endobj.
+{{object 6 0}} <<
+ {{streamlen}}
+>>
+stream
+BT
+20 50 Td
+/F1 12 Tf
+(Hello, world!) Tj
+0 50 Td
+/F2 16 Tf
+(Goodbye, world!) Tj
+ET
+endstream
+endobj
+{{object 5 0}} <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Helvetica
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/trailer_end_trailing_space.pdf b/testing/resources/trailer_end_trailing_space.pdf
new file mode 100644
index 0000000..e0ec6e4
--- /dev/null
+++ b/testing/resources/trailer_end_trailing_space.pdf
@@ -0,0 +1,99 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /MediaBox [0 0 200 200]
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Resources <<
+ /Font <<
+ /F1 4 0 R
+ /F2 5 0 R
+ >>
+ >>
+ /Contents 6 0 R
+>>
+endobj
+4 0 obj <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Times-Roman
+>>
+endobj
+% Single space after endstream and endobj.
+6 0 obj <<
+ /Length 83
+>>
+stream
+BT
+20 50 Td
+/F1 12 Tf
+(Hello, world!) Tj
+0 50 Td
+/F2 16 Tf
+(Goodbye, world!) Tj
+ET
+endstream
+endobj
+% Multiple spaces after endstream and endobj.
+6 0 obj <<
+ /Length 83
+>>
+stream
+BT
+20 50 Td
+/F1 12 Tf
+(Hello, world!) Tj
+0 50 Td
+/F2 16 Tf
+(Goodbye, world!) Tj
+ET
+endstream
+endobj
+% Tab after endstream and endobj.
+6 0 obj <<
+ /Length 83
+>>
+stream
+BT
+20 50 Td
+/F1 12 Tf
+(Hello, world!) Tj
+0 50 Td
+/F2 16 Tf
+(Goodbye, world!) Tj
+ET
+endstream
+endobj
+5 0 obj <<
+ /Type /Font
+ /Subtype /Type1
+ /BaseFont /Helvetica
+>>
+endobj
+xref
+0 7
+0000000000 65535 f
+0000000015 00000 n
+0000000068 00000 n
+0000000157 00000 n
+0000000299 00000 n
+0000000910 00000 n
+0000000774 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 7
+>>
+startxref
+986
+%%EOF
diff --git a/testing/resources/two_signatures.in b/testing/resources/two_signatures.in
new file mode 100644
index 0000000..f18495f
--- /dev/null
+++ b/testing/resources/two_signatures.in
@@ -0,0 +1,155 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /MediaBox [0 0 200 300]
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+>>
+endobj
+{{object 4 0}} <<
+ {{streamlen}}
+>>
+stream
+q
+0 0 0 rg
+0 290 10 10 re B*
+10 150 50 30 re B*
+0 0 1 rg
+190 290 10 10 re B*
+70 232 50 30 re B*
+0 1 0 rg
+190 0 10 10 re B*
+130 150 50 30 re B*
+1 0 0 rg
+0 0 10 10 re B*
+70 67 50 30 re B*
+Q
+endstream
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
+
+%% First incremental update adds an initial signature and update objects to
+%% refer to it.
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /AcroForm <<
+ /Fields [7 0 R]
+ /SigFlags 3
+ >>
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /Annots [7 0 R]
+>>
+endobj
+%% ByteRange is a pairs of integers (starting byte offset, length in bytes)
+{{object 5 0}} <<
+ /Type /Sig
+ /Filter /Adobe.PPKMS
+ /SubFilter /ETSI.CAdES.detached
+ /ByteRange [0 10 30 10]
+ /Contents <308006092A864886F70D010702A0803080020101>
+ /M (D:20200624093114+02'00')
+>>
+endobj
+{{object 6 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /BBox [0 0 0 0]
+ /Length 0
+>>
+stream
+endstream
+endobj
+{{object 7 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Sig
+ /F 132
+ /Rect [0 0 0 0]
+ /P 3 0 R
+ /T (Signature1)
+ /V 5 0 R
+ /DV 5 0 R
+ /AP <<
+ /N 6 0 R
+ >>
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
+
+%% Second incremental update adds a next signature and update objects once again to refer to it.
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /AcroForm <<
+ /Fields [7 0 R 10 0 R]
+ /SigFlags 3
+ >>
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /Annots [7 0 R 10 0 R]
+>>
+endobj
+{{object 8 0}} <<
+ /Type /Sig
+ /Filter /Adobe.PPKMS
+ /SubFilter /ETSI.CAdES.detached
+ /ByteRange [0 40 50 10]
+ /Contents <308006092A864886F70D010702A080308002010131>
+ /M (D:20200624093118+02'00')
+>>
+endobj
+{{object 9 0}} <<
+ /Type /XObject
+ /Subtype /Form
+ /BBox [0 0 0 0]
+ /Length 0
+>>
+stream
+endstream
+endobj
+{{object 10 0}} <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Sig
+ /F 132
+ /Rect [0 0 0 0]
+ /P 3 0 R
+ /T (Signature2)
+ /V 8 0 R
+ /DV 8 0 R
+ /AP <<
+ /N 9 0 R
+ >>
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/two_signatures.pdf b/testing/resources/two_signatures.pdf
new file mode 100644
index 0000000..9a033d6
--- /dev/null
+++ b/testing/resources/two_signatures.pdf
@@ -0,0 +1,195 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /MediaBox [0 0 200 300]
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+>>
+endobj
+4 0 obj <<
+ /Length 188
+>>
+stream
+q
+0 0 0 rg
+0 290 10 10 re B*
+10 150 50 30 re B*
+0 0 1 rg
+190 290 10 10 re B*
+70 232 50 30 re B*
+0 1 0 rg
+190 0 10 10 re B*
+130 150 50 30 re B*
+1 0 0 rg
+0 0 10 10 re B*
+70 67 50 30 re B*
+Q
+endstream
+endobj
+xref
+0 5
+0000000000 65535 f
+0000000015 00000 n
+0000000068 00000 n
+0000000157 00000 n
+0000000226 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 5
+>>
+startxref
+466
+%%EOF
+
+%% First incremental update adds an initial signature and update objects to
+%% refer to it.
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /AcroForm <<
+ /Fields [7 0 R]
+ /SigFlags 3
+ >>
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /Annots [7 0 R]
+>>
+endobj
+%% ByteRange is a pairs of integers (starting byte offset, length in bytes)
+5 0 obj <<
+ /Type /Sig
+ /Filter /Adobe.PPKMS
+ /SubFilter /ETSI.CAdES.detached
+ /ByteRange [0 10 30 10]
+ /Contents <308006092A864886F70D010702A0803080020101>
+ /M (D:20200624093114+02'00')
+>>
+endobj
+6 0 obj <<
+ /Type /XObject
+ /Subtype /Form
+ /BBox [0 0 0 0]
+ /Length 0
+>>
+stream
+endstream
+endobj
+7 0 obj <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Sig
+ /F 132
+ /Rect [0 0 0 0]
+ /P 3 0 R
+ /T (Signature1)
+ /V 5 0 R
+ /DV 5 0 R
+ /AP <<
+ /N 6 0 R
+ >>
+>>
+endobj
+xref
+0 8
+0000000000 65535 f
+0000000726 00000 n
+0000000068 00000 n
+0000000835 00000 n
+0000000226 00000 n
+0000000998 00000 n
+0000001201 00000 n
+0000001303 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 8
+>>
+startxref
+1475
+%%EOF
+
+%% Second incremental update adds a next signature and update objects once again to refer to it.
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /AcroForm <<
+ /Fields [7 0 R 10 0 R]
+ /SigFlags 3
+ >>
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Contents 4 0 R
+ /Annots [7 0 R 10 0 R]
+>>
+endobj
+8 0 obj <<
+ /Type /Sig
+ /Filter /Adobe.PPKMS
+ /SubFilter /ETSI.CAdES.detached
+ /ByteRange [0 40 50 10]
+ /Contents <308006092A864886F70D010702A080308002010131>
+ /M (D:20200624093118+02'00')
+>>
+endobj
+9 0 obj <<
+ /Type /XObject
+ /Subtype /Form
+ /BBox [0 0 0 0]
+ /Length 0
+>>
+stream
+endstream
+endobj
+10 0 obj <<
+ /Type /Annot
+ /Subtype /Widget
+ /FT /Sig
+ /F 132
+ /Rect [0 0 0 0]
+ /P 3 0 R
+ /T (Signature2)
+ /V 8 0 R
+ /DV 8 0 R
+ /AP <<
+ /N 9 0 R
+ >>
+>>
+endobj
+xref
+0 11
+0000000000 65535 f
+0000001801 00000 n
+0000000068 00000 n
+0000001917 00000 n
+0000000226 00000 n
+0000000998 00000 n
+0000001201 00000 n
+0000001303 00000 n
+0000002011 00000 n
+0000002216 00000 n
+0000002318 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 11
+>>
+startxref
+2491
+%%EOF
diff --git a/testing/resources/uri_action_nonascii.in b/testing/resources/uri_action_nonascii.in
new file mode 100644
index 0000000..d2ec540
--- /dev/null
+++ b/testing/resources/uri_action_nonascii.in
@@ -0,0 +1,44 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /MediaBox [0 0 200 200]
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Annots [4 0 R]
+>>
+endobj
+{{object 4 0}} <<
+ /A 5 0 R
+ /FT /Tx
+ /Ff 29360128
+ /Type /Annot
+ /Subtype /Link
+ /F 4
+ /Rect [1 1 199 199]
+ /BS <<
+ /W 1
+ /S /S
+ >>
+ /DA (/Helv 0 Tf 0 0 0 rg)
+ /V ()
+>>
+endobj
+{{object 5 0}} <<
+ /S /URI
+ /URI (https://example.com/\245octal\307chars)
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/uri_action_nonascii.pdf b/testing/resources/uri_action_nonascii.pdf
new file mode 100644
index 0000000..ffc084a
--- /dev/null
+++ b/testing/resources/uri_action_nonascii.pdf
@@ -0,0 +1,56 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /MediaBox [0 0 200 200]
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+ /Annots [4 0 R]
+>>
+endobj
+4 0 obj <<
+ /A 5 0 R
+ /FT /Tx
+ /Ff 29360128
+ /Type /Annot
+ /Subtype /Link
+ /F 4
+ /Rect [1 1 199 199]
+ /BS <<
+ /W 1
+ /S /S
+ >>
+ /DA (/Helv 0 Tf 0 0 0 rg)
+ /V ()
+>>
+endobj
+5 0 obj <<
+ /S /URI
+ /URI (https://example.com/\245octal\307chars)
+>>
+endobj
+xref
+0 6
+0000000000 65535 f
+0000000015 00000 n
+0000000068 00000 n
+0000000157 00000 n
+0000000226 00000 n
+0000000414 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 6
+>>
+startxref
+493
+%%EOF
diff --git a/testing/resources/viewer_pref_types.in b/testing/resources/viewer_pref_types.in
new file mode 100644
index 0000000..11b8034
--- /dev/null
+++ b/testing/resources/viewer_pref_types.in
@@ -0,0 +1,36 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /ViewerPreferences <<
+ /Bool true
+ /Num 1
+ /Str (str)
+ /Name /name
+ /Null null
+ /Ref 3 0 R
+ /EmptyArray []
+ /GoodArray [true 1 (str) /name]
+ /BadArray1 [true []]
+ /BadArray2 [1 <<>>]
+ /BadArray3 [/name 3 0 R]
+ /Dict <<
+ >>
+ >>
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+>>
+endobj
+{{xref}}
+{{trailer}}
+{{startxref}}
+%%EOF
diff --git a/testing/resources/viewer_pref_types.pdf b/testing/resources/viewer_pref_types.pdf
new file mode 100644
index 0000000..f74ca6f
--- /dev/null
+++ b/testing/resources/viewer_pref_types.pdf
@@ -0,0 +1,46 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /ViewerPreferences <<
+ /Bool true
+ /Num 1
+ /Str (str)
+ /Name /name
+ /Null null
+ /Ref 3 0 R
+ /EmptyArray []
+ /GoodArray [true 1 (str) /name]
+ /BadArray1 [true []]
+ /BadArray2 [1 <<>>]
+ /BadArray3 [/name 3 0 R]
+ /Dict <<
+ >>
+ >>
+>>
+endobj
+2 0 obj <<
+ /Type /Pages
+ /Count 1
+ /Kids [3 0 R]
+>>
+endobj
+3 0 obj <<
+ /Type /Page
+ /Parent 2 0 R
+>>
+endobj
+xref
+0 4
+0000000000 65535 f
+0000000015 00000 n
+0000000337 00000 n
+0000000400 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 4
+>>
+startxref
+451
+%%EOF
diff --git a/testing/resources/xfa/xfa_break_before_after.in b/testing/resources/xfa/xfa_break_before_after.in
new file mode 100644
index 0000000..1ff2f3a
--- /dev/null
+++ b/testing/resources/xfa/xfa_break_before_after.in
@@ -0,0 +1,54 @@
+{{header}}
+{{include ../xfa_catalog_1_0.fragment}}
+{{include ../xfa_object_2_0.fragment}}
+{{include ../xfa_preamble_3_0.fragment}}
+{{include ../xfa_config_4_0.fragment}}
+{{object 5 0}} <<
+ {{streamlen}}
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/3.0/">
+ <subform name="Form01" layout="tb" locale="en_ZA" restoreState="auto">
+ <pageSet>
+ <pageArea name="Page1" id="Page1" initialNumber="1">
+ <contentArea x="10.455mm" w="190.5mm" h="286mm"/>
+ <occur min="1" max="1"/>
+ </pageArea>
+ </pageSet>
+ <subform w="190.5mm" h="286mm" name="TForm1SubForm1">
+ </subform>
+ <subform w="190.5mm" h="286mm">
+ <breakBefore targetType="contentArea" />
+ </subform>
+ <subform w="190.5mm" h="286mm">
+ <breakAfter targetType="contentArea" />
+ </subform>
+ <subform w="190.5mm" h="286mm">
+ <breakBefore targetType="pageArea" />
+ </subform>
+ <subform w="190.5mm" h="286mm">
+ <breakAfter targetType="pageArea" />
+ </subform>
+ <subform w="190.5mm" h="286mm">
+ <breakBefore targetType="pageEven" />
+ </subform>
+ <subform w="190.5mm" h="286mm">
+ <breakAfter targetType="pageEven" />
+ </subform>
+ <subform w="190.5mm" h="286mm">
+ <breakBefore targetType="pageOdd" />
+ </subform>
+ <subform w="190.5mm" h="286mm">
+ <breakAfter targetType="pageOdd" />
+ </subform>
+ </subform>
+</template>
+endstream
+endobj
+{{include ../xfa_locale_6_0.fragment}}
+{{include ../xfa_postamble_7_0.fragment}}
+{{include ../xfa_pages_8_0.fragment}}
+{{xref}}
+{{trailer}}
+{{endxref}}
+%%EOF
diff --git a/testing/resources/xfa/xfa_break_before_after.pdf b/testing/resources/xfa/xfa_break_before_after.pdf
new file mode 100644
index 0000000..2235016
--- /dev/null
+++ b/testing/resources/xfa/xfa_break_before_after.pdf
@@ -0,0 +1,262 @@
+%PDF-1.7
+% ò¤ô
+1 0 obj <<
+ /AcroForm 2 0 R
+ /Extensions <<
+ /ADBE <<
+ /BaseVersion /1.7
+ /ExtensionLevel 8
+ >>
+ >>
+ /NeedsRendering true
+ /Pages 8 0 R
+ /Type /Catalog
+>>
+endobj
+2 0 obj <<
+ /XFA [
+ (preamble)
+ 3 0 R
+ (config)
+ 4 0 R
+ (template)
+ 5 0 R
+ (localeSet)
+ 6 0 R
+ (postamble)
+ 7 0 R
+ ]
+>>
+endobj
+3 0 obj <<
+ /Length 124
+>>
+stream
+<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/" timeStamp="2018-02-23T21:37:11Z" uuid="21482798-7bf0-40a4-bc5d-3cefdccf32b5">
+endstream
+endobj
+4 0 obj <<
+ /Length 642
+>>
+stream
+<config xmlns="http://www.xfa.org/schema/xci/3.0/">
+<agent name="designer">
+ <destination>pdf</destination>
+ <pdf>
+ <fontInfo/>
+ </pdf>
+</agent>
+<present>
+ <pdf>
+ <version>1.7</version>
+ <adobeExtensionLevel>8</adobeExtensionLevel>
+ <renderPolicy>client</renderPolicy>
+ <scriptModel>XFA</scriptModel>
+ <interactive>1</interactive>
+ </pdf>
+ <xdp>
+ <packets>*</packets>
+ </xdp>
+ <destination>pdf</destination>
+ <script>
+ <runScripts>server</runScripts>
+ </script>
+</present>
+<acrobat>
+ <acrobat7>
+ <dynamicRender>required</dynamicRender>
+ </acrobat7>
+ <validate>preSubmit</validate>
+</acrobat>
+</config>
+endstream
+endobj
+5 0 obj <<
+ /Length 1191
+>>
+stream
+<template xmlns="http://www.xfa.org/schema/xfa-template/3.0/">
+ <subform name="Form01" layout="tb" locale="en_ZA" restoreState="auto">
+ <pageSet>
+ <pageArea name="Page1" id="Page1" initialNumber="1">
+ <contentArea x="10.455mm" w="190.5mm" h="286mm"/>
+ <occur min="1" max="1"/>
+ </pageArea>
+ </pageSet>
+ <subform w="190.5mm" h="286mm" name="TForm1SubForm1">
+ </subform>
+ <subform w="190.5mm" h="286mm">
+ <breakBefore targetType="contentArea" />
+ </subform>
+ <subform w="190.5mm" h="286mm">
+ <breakAfter targetType="contentArea" />
+ </subform>
+ <subform w="190.5mm" h="286mm">
+ <breakBefore targetType="pageArea" />
+ </subform>
+ <subform w="190.5mm" h="286mm">
+ <breakAfter targetType="pageArea" />
+ </subform>
+ <subform w="190.5mm" h="286mm">
+ <breakBefore targetType="pageEven" />
+ </subform>
+ <subform w="190.5mm" h="286mm">
+ <breakAfter targetType="pageEven" />
+ </subform>
+ <subform w="190.5mm" h="286mm">
+ <breakBefore targetType="pageOdd" />
+ </subform>
+ <subform w="190.5mm" h="286mm">
+ <breakAfter targetType="pageOdd" />
+ </subform>
+ </subform>
+</template>
+endstream
+endobj
+6 0 obj <<
+ /Length 3455
+>>
+stream
+<localeSet xmlns="http://www.xfa.org/schema/xfa-locale-set/2.7/">
+ <locale name="en_US" desc="English (United States)">
+ <calendarSymbols name="gregorian">
+ <monthNames>
+ <month>January</month>
+ <month>February</month>
+ <month>March</month>
+ <month>April</month>
+ <month>May</month>
+ <month>June</month>
+ <month>July</month>
+ <month>August</month>
+ <month>September</month>
+ <month>October</month>
+ <month>November</month>
+ <month>December</month>
+ </monthNames>
+ <monthNames abbr="1">
+ <month>Jan</month>
+ <month>Feb</month>
+ <month>Mar</month>
+ <month>Apr</month>
+ <month>May</month>
+ <month>Jun</month>
+ <month>Jul</month>
+ <month>Aug</month>
+ <month>Sep</month>
+ <month>Oct</month>
+ <month>Nov</month>
+ <month>Dec</month>
+ </monthNames>
+ <dayNames>
+ <day>Sunday</day>
+ <day>Monday</day>
+ <day>Tuesday</day>
+ <day>Wednesday</day>
+ <day>Thursday</day>
+ <day>Friday</day>
+ <day>Saturday</day>
+ </dayNames>
+ <dayNames abbr="1">
+ <day>Sun</day>
+ <day>Mon</day>
+ <day>Tue</day>
+ <day>Wed</day>
+ <day>Thu</day>
+ <day>Fri</day>
+ <day>Sat</day>
+ </dayNames>
+ <meridiemNames>
+ <meridiem>AM</meridiem>
+ <meridiem>PM</meridiem>
+ </meridiemNames>
+ <eraNames>
+ <era>BC</era>
+ <era>AD</era>
+ </eraNames>
+ </calendarSymbols>
+ <datePatterns>
+ <datePattern name="full">EEEE, MMMM D, YYYY</datePattern>
+ <datePattern name="long">MMMM D, YYYY</datePattern>
+ <datePattern name="med">MMM D, YYYY</datePattern>
+ <datePattern name="short">M/D/YY</datePattern>
+ </datePatterns>
+ <timePatterns>
+ <timePattern name="full">h:MM:SS A Z</timePattern>
+ <timePattern name="long">h:MM:SS A Z</timePattern>
+ <timePattern name="med">h:MM:SS A</timePattern>
+ <timePattern name="short">h:MM A</timePattern>
+ </timePatterns>
+ <dateTimeSymbols>GyMdkHmsSEDFwWahKzZ</dateTimeSymbols>
+ <numberPatterns>
+ <numberPattern name="numeric">z,zz9.zzz</numberPattern>
+ <numberPattern name="currency">$z,zz9.99|($z,zz9.99)</numberPattern>
+ <numberPattern name="percent">z,zz9%</numberPattern>
+ </numberPatterns>
+ <numberSymbols>
+ <numberSymbol name="decimal">.</numberSymbol>
+ <numberSymbol name="grouping">,</numberSymbol>
+ <numberSymbol name="percent">%</numberSymbol>
+ <numberSymbol name="minus">-</numberSymbol>
+ <numberSymbol name="zero">0</numberSymbol>
+ </numberSymbols>
+ <currencySymbols>
+ <currencySymbol name="symbol">$</currencySymbol>
+ <currencySymbol name="isoname">USD</currencySymbol>
+ <currencySymbol name="decimal">.</currencySymbol>
+ </currencySymbols>
+ <typefaces>
+ <typeface name="Myriad Pro"/>
+ <typeface name="Minion Pro"/>
+ <typeface name="Courier Std"/>
+ <typeface name="Adobe Pi Std"/>
+ <typeface name="Adobe Hebrew"/>
+ <typeface name="Adobe Arabic"/>
+ <typeface name="Adobe Thai"/>
+ <typeface name="Kozuka Gothic Pro-VI M"/>
+ <typeface name="Kozuka Mincho Pro-VI R"/>
+ <typeface name="Adobe Ming Std L"/>
+ <typeface name="Adobe Song Std L"/>
+ <typeface name="Adobe Myungjo Std M"/>
+ </typefaces>
+ </locale>
+</localeSet>
+endstream
+endobj
+7 0 obj <<
+ /Length 11
+>>
+stream
+</xdp:xdp>
+endstream
+endobj
+8 0 obj <<
+ /Type /Pages
+ /Count 1
+ /Kids [9 0 R]
+>>
+endobj
+9 0 obj <<
+ /Type /Page
+ /Parent 8 0 R
+ /MediaBox [0 0 612 792]
+>>
+endobj
+xref
+0 10
+0000000000 65535 f
+0000000015 00000 n
+0000000199 00000 n
+0000000358 00000 n
+0000000534 00000 n
+0000001228 00000 n
+0000002472 00000 n
+0000005980 00000 n
+0000006042 00000 n
+0000006105 00000 n
+trailer <<
+ /Root 1 0 R
+ /Size 10
+>>
+{{endxref}}
+%%EOF
diff --git a/testing/resources/xfa/xfa_combobox.pdf b/testing/resources/xfa/xfa_combobox.pdf
index 0989361..a4b606c 100644
--- a/testing/resources/xfa/xfa_combobox.pdf
+++ b/testing/resources/xfa/xfa_combobox.pdf
@@ -224,7 +224,7 @@
endobj
9 0 obj <<
/Type /Page
- /Parent 2 0 R
+ /Parent 8 0 R
/MediaBox [0 0 612 792]
>>
endobj
diff --git a/testing/resources/xfa/xfa_date_time_edit.pdf b/testing/resources/xfa/xfa_date_time_edit.pdf
index 04ea239..33f02bd 100644
--- a/testing/resources/xfa/xfa_date_time_edit.pdf
+++ b/testing/resources/xfa/xfa_date_time_edit.pdf
@@ -219,7 +219,7 @@
endobj
9 0 obj <<
/Type /Page
- /Parent 2 0 R
+ /Parent 8 0 R
/MediaBox [0 0 612 792]
>>
endobj
diff --git a/testing/resources/xfa/xfa_image_edit.in b/testing/resources/xfa/xfa_image_edit.in
index 0e36fdd..a3bdae6 100644
--- a/testing/resources/xfa/xfa_image_edit.in
+++ b/testing/resources/xfa/xfa_image_edit.in
@@ -21,7 +21,9 @@
<imageEdit data="embed"/>
</ui>
<value>
- <image contentType="image/jpg">FOOOEY</image>
+ <image contentType="image/jpg">
+ {{include ../mona_lisa.fragment}}
+ </image>
</value>
</field>
</subform>
diff --git a/testing/resources/xfa/xfa_image_edit.pdf b/testing/resources/xfa/xfa_image_edit.pdf
index 7496367..d35ef5d 100644
--- a/testing/resources/xfa/xfa_image_edit.pdf
+++ b/testing/resources/xfa/xfa_image_edit.pdf
@@ -72,7 +72,7 @@
endstream
endobj
5 0 obj <<
- /Length 667
+ /Length 9005
>>
stream
<template xmlns="http://www.xfa.org/schema/xfa-template/3.3/">
@@ -89,7 +89,117 @@
<imageEdit data="embed"/>
</ui>
<value>
- <image contentType="image/jpg">FOOOEY</image>
+ <image contentType="image/jpg">
+/9j/4AAQSkZJRgABAQEAZABkAAD//gBSRmlsZSBzb3VyY2U6IGh0dHA6Ly9jb21tb25zLndpa2lt
+ZWRpYS5vcmcvd2lraS9GaWxlOk1vbmFfTGlzYV9mYWNlXzgwMHg4MDBweC5qcGf/2wBDAAYEBQYF
+BAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUo
+KSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgo
+KCgoKCgoKCgoKCgoKCj/wAARCAB4AHgDAREAAhEBAxEB/8QAHAAAAgIDAQEAAAAAAAAAAAAABQYE
+BwEDCAIA/8QAOxAAAgECBQIEBAQFAwMFAAAAAQIDBBEABRIhMQZBEyJRYQcycYEUkaGxFUJiwfAj
+JOEWF9ElM1LC8f/EABoBAAIDAQEAAAAAAAAAAAAAAAIDAQQFAAb/xAAvEQACAgICAgAEBQQCAwAA
+AAAAAQIRAyESMQRBBRMiUTJhcaGxFCNSgZHwweHx/9oADAMBAAIRAxEAPwBqiIChT5t7gEYwX1ss
+JUbY0ErKBHpFu498Q3fRKTMmJVNm2POo9/tiG2kTFG+FfIAq9t2ta2BtvsKtkqKmDE2UgMDiUn2C
+3ugTnvVGRdPy6M2zKFJxv+HivLN6i6LuPvbDsalPaRPy29Cw/wAYOn1kPgUmYS3OxZUjJvwbFrgY
+fHx8hziqCVF8V+namVUnSvojzqlp9SkD3QkYGWKa62SsY5ZVmVDmlMKnLamCqgvu8bBgPYjkH64U
+3x/FpkcWF4yT8oG4337YBzdUgVHezEwN1N1BxzurslJGI1uSpJG3bvjlew2bYUN+fS+Cj12DJkqE
+WNgOeL4OM66AaNscXkb2vziVK47ZD0IkcG1r/X/PthVWFZLRNKqAAdiCT9cC19jk77MRpqILKNjf
+i/tbCpcug40SooAz6tNyfLsOPbBQhslsrH4n/ECXLa+Tp7IZvBrxaOprEXU0Fx8if1+/a/ri3i8e
+1zn1/P8A6Ba6oR8h6YqY8szKshpauaWdNCzBHkZifmYk778ffAZvIU5Qg3pMsQg4xbS2KmcZQKPU
+aqmqItVhqeEqL9rnGhjzc3SaZXyY63QGDvSuJaScofVfMp57YsOPLUkLUnHcQplXUFfQZrFV5bK2
+XZkll8SEWRx6MnBB9D+mEywppqW0N+by/U6Y+EvXdP1rlrJKkVNnVMAtVTLsrDgSJ30nj2O3pjLz
+4XhlXr0d+JWh6eJQCy2b6DEOKAT+55hQrYsNrW98TH2S2ToAATYG5GwI4wUqSYPZuSEBNXAIBO2E
+8eO0TZ8UZVtf5hvbE7RHYkQRG6kix7f2wuMnexjr0SvDZiqpf6dsH30D12bIKe77gna7W7bYHbbC
+tIFdf9R0/R3S9VmcrIJFHh06t/PKR5Rbk+v0GLMISk1CK2wE03begP8ACHommhy6LM8zjhqM0qf9
+eeoljEjhm3sL7D7b++KmXK8+Tin9K0l+hcUflwTrbLXFHCABftYdvtthbjRHNgjOsoiqKaaJ9Tqy
+kFWAKn88Vsip6HwlfZzV8R+kKeikeSjS128wCD+2NT4f57yfTP0I8jCltFWyJJEXul0vYkLce4Jx
+tqSfRRoJ9K5/WdNdSUOb5a7iWmbUVPEkZI1o3qCt/wBMBmxLLjcP+P1IjOpWztvJa+lzTJ6PMqF9
+dJVxLLG47qwuPpbi2MqP0Opd9ByV9E2KNWF97AbNiWlu2crN8cIABIFxyL7YW2yTZGhlNr7eltsc
+nZD0ZkpyI++1+cMcVVg3sUUWMxRtHpbVc3GKr4voYrM063k32YEi9t9zhiTXRDJVHAzSB3uoK2F/
+bEwm4u7OaT0UL8cs9TqHPsqyZEH+nWxaWvuus7KB6keYn6Dti5gySm5Zn0kyflrGlBdtl7dNWp6F
+FY2awU39RjFxx1aNHJ9g6pdWa5LX3J9MS27aE0uyHXiSRCFN9rG3OEThyGxaQi9UdOpXwyCW1muD
+b/PbC4J4ZckNdTVM5u63y5cpzeemilCQDzebuff9bY9R4eV5cak+zMzxUJUhVmbUNQt6W9bC++Lp
+WaSOjvgT1JK/w+ho5mtHl9Y1OD6o1pB9hrP5Yy/Mhxm697Gp3Wy0aXNhNMsIYi29u1j2v37Yq7fZ
+LetB01arE5DKVQaRY8m+/wDbHUxTnvQRplvbcaTuL+/GIXdMY2bwbxFd2wXNtUD07KMyvqid3WGn
+LvqszF0PIB8o/LfEairZNT9D109mYzSmMiRaJUkMbjmxIDCx+hwacpNEKadk3Nq38HlE8oGpvw7s
+thvcKdsLlG6TCUt0jkbrCsaXramzCGSNr10MmkfNrUqtxfsQBtjS8eP9hwf2f7jJ25KS+6Lv/wC6
+VBDlQq0lSipEm/DhqpZSddr2IRG08H5iDtxjGXi5XJQjt1fa6L7yQrkx1n6p0dKSZwIwAE4vca+B
+Y973B++KXJyfFd9B/LS2xLTq7NKbIWzvOfHpaeRxGFWkknILfKCFsBe3rg/lKWR48cr/ADbpHcmo
+3JAOLrjqHPcwio8uyeaohcj/AHUaSJGBv5jrUabW4P64tT8XHjjynk39tX+wr5km6jErP4oUM8Od
++JUfM/zC99/rjT+GZYvHSRV8lNSViSmmF2XUrM1wQefe2NTsp7uix/hJWFKDNaUMViM0E5AYgsNJ
+Ui/2GKHmR/C/ZPWi5MkzgykxMYlABszCxuP32xVUFsGWR+w1Lm0cNJHGAJRIw3U302bfbne2Ipds
+SOXT9UKqEsHLJqBH0HbFKVqSTLqaadByJ/D1FgbW2vhuO07BbXRydQZocvqqclJCW31XA3IsSe1z
+fbFiWHndC1krQfyLrl6TI5KKFNFTGDaULfU1yGJ/NQCe2AyYJJ/T0Hjkktk2l6tmoTmVI0zSrBEq
+wySG5RmZthc8AXIxPy3KCZy1Oijxoi67yiKUf7eHNI1Z73DDxRck+v8A4xou/wCnlXbi/wCBqf1x
+17OwP4TBW05RJqiJTJ4jBQu7X3O4Nj7jHmFBTjyTNNzcHRo6ioUk6ckprBYGlBFxfYf/AIMdwaWi
+LTeyblGXRnLYYWqaiOwsdDDf87jELFyVSOlNx/Caa5abKKdjCZGL3LNI5YnC5pQdRQULkrZyz8Yc
+wNZ1G3hFrIeB2/y2PRfCYccNv2Z/lu5UhEcuXErWYHdibbE/2xq1ZRHz4VpNP1BVU1I0i+PRlyq2
+a5V1P/2/XFPyopQTfoJLk9Fy5TRVkMjvLTVMpA3ZrHew/I/bvijyW9kSg0uiWIK2PSYaGpYA7Cwv
+xyT3xzURLvsaukq2opJSKiCWJGJtZCdz9sJlFdpjMc2tMbJMxQqbyMCb8jAPI1sclZyPmMBZ2ZZW
+GkeJqLaix4A+mNODr0IaNbMYiDDJIAbgMBbUpAO4F9r3/K+J2yaT7AGeVdTYtHIG1Pu3qTcbn7n8
+sWMUVQSb5C3mFayrGYXa8bhyrdiDdbHvxh0YJ6GznpUdk9I9QJmuUUciOC00aEXO1iAf748jNPHc
+H6dGy6nUgN8QOuafpqloqGty+tkr5m3iiFw68albhhxxv64b4+CedaaSX3ETlHG7e7GLpPMzU9OU
+lZVRS0krhv8AQlPnUajpv72thTSg2rug/wASTFzrTNZTCywgkr9sLjHnJX6Dk6WjmLrCoaozeWR7
+sdR2btvwceq8VVjSRlZvxAaWcNEpK2f+axPGLCEMNdLVMkWYRmJn8UkqpHuLf87emByxuLvoh7Lb
+ynM8wphrSSrjB8xW1lJ2/wA7YoNKnQDeg7H1DXooAmrFckWUbn9rdsQ8a9CqDtB1XUJvUzh1202s
+Dv625tgJwXVEptK7CY6jlqoisTygggGy7geuFcFdkcm/pbOdpa7xpQzCXzeQG2s27WONCMKHHppn
+lHhqg1KbhhtfjkG37euJBe2Q81y92y8vM6HUy2AI8p3P9t//ADgoZVy0iYJ3QqThI4NBVZJmc3bk
+qB6fXFlNt2Ntca9l5fA7qWlzTp6PJ6ipNPmVIDGrgjV4d/Kwvza9vtjznxXBKGR5K+mX8ml4mVTg
+oe0WR1OlR4UUZrcwqHUXSdcvjk29yLAfljN0pW/5LXoh5DA1NMZszrKyrn07LKiokYHoFHJuNyTi
+ZSjJrVV+oKX52LvXGcwDxI42BYgjbfe22H+Pi5y2LyTpUc/Z9KZcwZ2Yl2a/t7DHqMKSjSMvK9gy
+VlvZCRtp+vqcNQtumFMhDfjIWifS9wb3taxwvJJKLOirLlpljjdYiulnF/KfNuNhx9+cZnPTdC3H
+VBCSjUxqZFOlPMCU77knb2xHNN7YFM30ckEQIAXTfY6fMLd8Tr2CyfJnsUKlIULNfsPl55PpxiJQ
+V2FG0ikqKoLlRpRlbSCjC4O/Fr8k+98aEo/caHaWYyOVMOXBRIWCSHYj6ffCZxXabIoL5hl0/wD0
+5WySJQNEkQmURhiWCkG1jyQL++EJrmtjIqip8x8JpnnkRXZyQNPC27n12tjSiq0TrtkGOtlocziq
+sullp54SGjkDWa+3+Wwc4KUeM9pkW4SuLOi+kfjrlz9PxwZ0r0+ZIgWTSl0f+ofX0x5/P8Kywf8A
+a3H+DRx+VCf4tMEZ/wDEuOoB/A6UjI80kjC/5emBx/DZ95Dp511ErvMuojVvppr1EzXJfgL32vzj
+Sx+Lw70V5ZL62LVXSy+O5ZjIxNy9tgMXLVUV3FoinQiG+7WNiB++D7AJuWzGJ1K3BUcn7EYiULVE
+xdOy6qLNoTl1LIWEr2XSVIsGNr77d7jb9cZrwtXE7l7DdVXxVETeV4ybKoQ727m3bfCfkxUrBcrV
+UQBXwq7GJZmXRqKActf0+ww2Ka0C4Jn1TnETVI0sCC1iBGPU/p+3ptjpJvs5QS9i1Q0IVUctAlmu
+PCjB/ckg4a5xYwPUpiiZ1srPqHnbw9TahyABzta+Ikv8URFm+oqA0Eo8VDGV0MqkEDm4P14++EO0
+9DE12UbnlGtPn1VTh2aNJLIVTc+VSARtv5gMa2KVwUgX2BZUu6nURcXJY73w7sGSoO9ARUcnWuUQ
+5giyUk1QIXRvl84Ki/3IxW8vl8mTi6dB4GlkXItXqb4WU8JY0EcSWO5AuAMZGH4nKL4z2Wsnjp7R
+AynonTIgnjUpcCygj8v3wefzk1pnQxv2TOrOlBT5S/gRqiqoJ2tbfck+mEeN5vLJ9TGSxaKlqKfw
+igdGUgHy25B4xvRmn0UZRNog8GjSWQeQFjbttYYlTTlR0o0rLL6OzCiqMshK1FPSLpWO7garj5u3
+YnnFDKppvt//AEFRTHVYacgQ0udQSMynWGS4tzzqxUtydyQTiq0YfpseHLWawaYC6u3kPoCvub4O
+OWvpbI4ezbTdJxC0r1XhIx1aW8oUn1v/AJvivPzI39DsdHx5PtUVPoneOVpayRKlKZfCaJiCdyTv
+ck2F/uMaXKnpavYiiMnW1Y9OwqxTtO6C0ynQoIuo1AA78/8AGHy8aL6YN0eM16rr5qeHxggpp0Cz
+aYwLMrndTyLGx9MQvHjvfR1+hPramevr6iaSZpJJZS2vgsext9hixGKjFJE22fZbltZmtctLQwy1
+FQ+yJGNRv+1vfHTyQhHnkdImMJSfFLZevw3+EUcEyV2czh6lfkVR5EbtYnk+/tjB8n4m8v0YVS/c
+u4vGUPql2XFJl5MAWYaibD5f8tjIk0XEjNNkSIxa2jfa/wCWBa+xNkHPMnSajdbgEIwIbv6XHpgV
+qXJBJXo5zqsnEWdGjIWS8oVSG+XU9h9bb3HbHpoZ+WHn+RQljqVEHqOkENFDSqiMq1UxW3JF7H7X
+Xj64d487lyv0hWRar8wJk0arVCJ2RUJJ1MdlbixPobYt5HcW0V0iysiyTMa6ro2pqOi0RvdHjk1K
++2wIBJIxk5pwjabewoxd2hpHRvU1TmFFXVGZapoTdjqKge3Nj2H0AxXl5eOMWlHsOOCTY7ZjUTUm
+VwrLL+JrVGyKCdbD1P8ALz3OMzDH63JaTLkp8Y17KG6k8OOKopKGIvII1GuxBtxb2G55/vj0HjqT
+lym/ZTlVUhPqqA/wWREVS8Uha+m+w2IB/LjGgprmhDTI2awuuVUxmLqI49Kqex1E2PvucdGVzdE1
+SsG0ccktSqxX1Dvbgep/PBt0rOirejpj4C9IJQ5TLWTxXnqW1K9iCYx8ot6Hn7jHmfifkPNlUU9L
++TVwY/lw/Nl009Ivg2CgWsRccHFSNpWiW7dGWijuBGLXbAPYa12bWj0wGx78WxMlUQU9i91AqpSz
+CUnwyp498VcjldD8Zz7EIaTOqd6dkll/EukjWuCQbkqSeNyfXtvj0D5TxvkvVlZ1FiP1DXvJmwic
+qRCWN/fUf73xqePjUcdr2UMsvqPa5LVVVG88dNK8RYBNKni3Jt62wXz4xbi2FxbV0Fum80qKevSn
+qZXpp1kChilib8bYR5GFSi5R2LafRfGW9RR1FHFBUxzyGMJTuIxpAfgAXtfb19748/ODT2WYz0e6
+l6GOnWVzXxr4nhp8urUb3BBa/wBzhP1ctfuE6rZVOe0aNCskauXSO7Kw0rIO2/pe36Y1sGRp02Vp
+xdWKeeS02V5PGs0aLUMRaPuSMaGLnlyWnoBpRVsQayplq5DJMQVDHSnZfYY0IxSWhTdlp/B3pMZt
+RzVksPiXa2+1gP2A2NxvjH+JeY8clCJf8TEmuTOm+k8pGU5VBT69ciLubW1H1AxhOTcm37LcpWHw
+BYDa/e+Du9AL7mDGRp3IOq9sQ4vom/Z9UJqW1rEbHvgZHJ7EvrVo48qqBJJa6FTcHk9vU4RdzikW
+I9MpSopZ6itCUdGoUEkuyhUDXW9zfbgbcXxtQmlG5S3/AOCrkT6iiZknw5paurmq5JRmFbK5Z1SI
+yxq1727Ltfe57YXk+JTSUIql+4C8ZW5NhvOsloaOkZMwzGKIR/8AtJNVrCL8GyxrueRtfAYcuSTk
+4x3+l/yFOMUtspvq5MtjrUejqF8YfM0c0rgenzqP0ON7xnkcWpr+DPy1eiT0v17XZUwhqJmeEEBJ
+lVQwP9RNwdu/IwryPAhkuUdMnHlcdMtTJ/iHPDCGzFEZJPKr+EmpbDctpA59TvjMn4bb+mi1GQKp
+0myd2rM8rqengC6pUldW2K7WUEkX+vNhbA5az/Rhi2/0ohfRuWim+r83Gd59PVxK0dKvkp47Wsg9
+vfnHofGwfIxqDdv2Uck+UrQMjjaQrCpbzDg84baQNM6J+Due02Vwfgc5XRS2DxS6SFF+dQ/Kx+2P
+M/EcDlk+bD/Zq+PlShxZfFPXUtTRGqpqiOSMjV4isCCPrx3xm8lX2D4uyTSFpRrIIU4OKs6TrROA
+TQe/IGHqOhLbs1SeGgYHn62H1wuSDTbK862rqGOOZDLrYA/JJpAJG4v22v72xWWJzmmh/wAxQVMq
+PMetMloJJHaMZhU2IjhtaFe3H83bnGvj+HZsi/xX7lV+RFO3sWM++JOdVkfgzVy5fSqDppqRLEDi
+5tsP0Pti/g+F4obrl+bEy8lvrQjVuc1Uxv8AiZyp/pA3+u+NKOCEVVFdzb7YPeeRyLs7G9zra+DS
+SAb9mI9QG1irHcDg46jhx6Unlqf/AEzYTIC8bjctYElSODtc7+n0xUz41H+5/wB/UtYJuX0P/v5A
+3rmeq/i38PqleMU41OjC12Nz+gNh9cT4UY8OcfYnNafF+hZbbzXv7W74t0IDmQQJJI81TfSGAuDx
+te+/+cYRlbWkGl9xsjzYitpIqYMksqDQNfyKCbk32vYHn/5DFT5Sabl6GqdPQcyjNJTI6ZfO1KsV
+tbU8ugsx33HDADfj88V8mBNXJXY6GV9Idss+IWf5epi/iaT6AAyVFOCRe1hdbeo5v2xVfiQ7imv9
+jVlT7C8HxZro95IKCcKbF4y4J/Q/TAf0kv8AIJzh3REz74pVE1NIbU9DDGt5JSS+/ZFIFy1+w3+g
+3wEfDcpV2BLyIpfSVD1D1f40FU5m8JpzaOJV8SZ151MSdMYNztuxxq4PD4ta6/4/19ytPNYjPXSy
+E+CCt99iSxPqTycaKikV07C2X5bClI1bVgtHTjdb7SPvZfzt+uFSnLkox7YVLshVcaRKseka7NyN
+gbL/AM4bFtnPQLWOQm6q5ABJ8p49T7e+D5AbZ6VvmNh2sL8Y6yVYRy+qemqaadLiSFw4sObdt8RJ
+cotP2HGTi7XZ/9k=
+ </image>
</value>
</field>
</subform>
@@ -222,7 +332,7 @@
endobj
9 0 obj <<
/Type /Page
- /Parent 2 0 R
+ /Parent 8 0 R
/MediaBox [0 0 612 792]
>>
endobj
@@ -234,14 +344,14 @@
0000000358 00000 n
0000000534 00000 n
0000001228 00000 n
-0000001947 00000 n
-0000005455 00000 n
-0000005517 00000 n
-0000005580 00000 n
+0000010286 00000 n
+0000013794 00000 n
+0000013856 00000 n
+0000013919 00000 n
trailer <<
/Root 1 0 R
/Size 10
>>
startxref
-5657
+13996
%%EOF
diff --git a/testing/resources/xfa/xfa_multiline_textfield.pdf b/testing/resources/xfa/xfa_multiline_textfield.pdf
index 6b7eae4..eedd6a2 100644
--- a/testing/resources/xfa/xfa_multiline_textfield.pdf
+++ b/testing/resources/xfa/xfa_multiline_textfield.pdf
@@ -221,7 +221,7 @@
endobj
9 0 obj <<
/Type /Page
- /Parent 2 0 R
+ /Parent 8 0 R
/MediaBox [0 0 612 792]
>>
endobj
diff --git a/testing/resources/xfa_pages_8_0.fragment b/testing/resources/xfa_pages_8_0.fragment
index ce089c4..2a8a910 100644
--- a/testing/resources/xfa_pages_8_0.fragment
+++ b/testing/resources/xfa_pages_8_0.fragment
@@ -6,7 +6,7 @@
endobj
{{object 9 0}} <<
/Type /Page
- /Parent 2 0 R
+ /Parent 8 0 R
/MediaBox [0 0 612 792]
>>
endobj
diff --git a/testing/scoped_set_tz.cpp b/testing/scoped_set_tz.cpp
new file mode 100644
index 0000000..06b70dc
--- /dev/null
+++ b/testing/scoped_set_tz.cpp
@@ -0,0 +1,44 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/scoped_set_tz.h"
+
+#include <stdlib.h>
+#include <time.h>
+
+#include "build/build_config.h"
+#include "third_party/base/check_op.h"
+
+namespace {
+
+constexpr char kTZ[] = "TZ";
+
+#if BUILDFLAG(IS_WIN)
+#define SETENV(name, value) _putenv_s(name, value)
+#define TZSET _tzset
+#define UNSETENV(name) _putenv_s(name, "")
+#else
+#define SETENV(name, value) setenv(name, value, 1)
+#define TZSET tzset
+#define UNSETENV(name) unsetenv(name)
+#endif
+
+} // namespace
+
+ScopedSetTZ::ScopedSetTZ(const std::string& tz) {
+ const char* old_tz = getenv(kTZ);
+ if (old_tz)
+ old_tz_ = old_tz;
+
+ CHECK_EQ(0, SETENV(kTZ, tz.c_str()));
+ TZSET();
+}
+
+ScopedSetTZ::~ScopedSetTZ() {
+ if (old_tz_.has_value())
+ CHECK_EQ(0, SETENV(kTZ, old_tz_.value().c_str()));
+ else
+ CHECK_EQ(0, UNSETENV(kTZ));
+ TZSET();
+}
diff --git a/testing/scoped_set_tz.h b/testing/scoped_set_tz.h
new file mode 100644
index 0000000..5928958
--- /dev/null
+++ b/testing/scoped_set_tz.h
@@ -0,0 +1,26 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TESTING_SCOPED_SET_TZ_H_
+#define TESTING_SCOPED_SET_TZ_H_
+
+#include <string>
+
+#include "core/fxcrt/fx_memory.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+class ScopedSetTZ {
+ public:
+ FX_STACK_ALLOCATED();
+
+ explicit ScopedSetTZ(const std::string& tz);
+ ScopedSetTZ(const ScopedSetTZ&) = delete;
+ ScopedSetTZ& operator=(const ScopedSetTZ&) = delete;
+ ~ScopedSetTZ();
+
+ private:
+ absl::optional<std::string> old_tz_;
+};
+
+#endif // TESTING_SCOPED_SET_TZ_H_
diff --git a/testing/string_write_stream.cpp b/testing/string_write_stream.cpp
index 53141eb..1a99d9a 100644
--- a/testing/string_write_stream.cpp
+++ b/testing/string_write_stream.cpp
@@ -1,8 +1,9 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "testing/string_write_stream.h"
+
#include "core/fxcrt/bytestring.h"
#include "core/fxcrt/widestring.h"
@@ -10,23 +11,7 @@
StringWriteStream::~StringWriteStream() = default;
-FX_FILESIZE StringWriteStream::GetSize() {
- return stream_.tellp();
-}
-
-bool StringWriteStream::Flush() {
- return true;
-}
-
-bool StringWriteStream::WriteBlockAtOffset(const void* pData,
- FX_FILESIZE offset,
- size_t size) {
- ASSERT(offset == 0);
- stream_.write(static_cast<const char*>(pData), size);
- return true;
-}
-
-bool StringWriteStream::WriteString(ByteStringView str) {
- stream_.write(str.unterminated_c_str(), str.GetLength());
+bool StringWriteStream::WriteBlock(pdfium::span<const uint8_t> buffer) {
+ stream_.write(reinterpret_cast<const char*>(buffer.data()), buffer.size());
return true;
}
diff --git a/testing/string_write_stream.h b/testing/string_write_stream.h
index 08a2174..fc4dc02 100644
--- a/testing/string_write_stream.h
+++ b/testing/string_write_stream.h
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -10,18 +10,13 @@
#include "core/fxcrt/fx_stream.h"
-class StringWriteStream final : public IFX_SeekableWriteStream {
+class StringWriteStream final : public IFX_RetainableWriteStream {
public:
StringWriteStream();
~StringWriteStream() override;
- // IFX_SeekableWriteStream
- FX_FILESIZE GetSize() override;
- bool Flush() override;
- bool WriteBlockAtOffset(const void* pData,
- FX_FILESIZE offset,
- size_t size) override;
- bool WriteString(ByteStringView str) override;
+ // IFX_WriteStream:
+ bool WriteBlock(pdfium::span<const uint8_t> buffer) override;
std::string ToString() const { return stream_.str(); }
diff --git a/testing/test.gni b/testing/test.gni
index 5a8505f..6ad2c2d 100644
--- a/testing/test.gni
+++ b/testing/test.gni
@@ -1,4 +1,4 @@
-# Copyright 2015 The Chromium Authors. All rights reserved.
+# Copyright 2015 The PDFium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -14,6 +14,7 @@
template("test") {
if (is_android) {
import("//build/config/android/config.gni")
+ import("//build/config/android/internal_rules.gni")
import("//build/config/android/rules.gni")
_use_raw_android_executable = defined(invoker.use_raw_android_executable) &&
@@ -53,12 +54,9 @@
# the default shared_library configs rather than executable configs.
configs -= [
"//build/config:shared_library_config",
- "//build/config/android:hide_all_but_jni_onload",
- ]
- configs += [
- "//build/config:executable_config",
"//build/config/android:hide_all_but_jni",
]
+ configs += [ "//build/config:executable_config" ]
# Don't output to the root or else conflict with the group() below.
output_name = rebase_path(_exec_output, root_out_dir)
@@ -68,26 +66,41 @@
testonly = true
dist_dir = "$root_out_dir/$target_name"
binary = _exec_output
- deps = [
- ":$_exec_target",
- ]
+ deps = [ ":$_exec_target" ]
if (defined(invoker.extra_dist_files)) {
extra_files = invoker.extra_dist_files
}
}
} else {
- _library_target = "_${target_name}__library"
- _apk_target = "${target_name}_apk"
+ _library_target = "${target_name}__library"
+ _apk_target = "${target_name}__apk"
_apk_specific_vars = [
"android_manifest",
"android_manifest_dep",
"enable_multidex",
+ "product_config_java_packages",
+ "min_sdk_version",
"proguard_configs",
"proguard_enabled",
+ "shared_resources",
+ "srcjar_deps",
+ "target_sdk_version",
"use_default_launcher",
- "write_asset_list",
"use_native_activity",
]
+
+ # Adds the unwind tables from unstripped binary as an asset file in the
+ # apk, if |add_unwind_tables_in_apk| is specified by the test.
+ if (defined(invoker.add_unwind_tables_in_apk) &&
+ invoker.add_unwind_tables_in_apk) {
+ _unwind_table_asset_name = "${target_name}_unwind_assets"
+ unwind_table_asset(_unwind_table_asset_name) {
+ testonly = true
+ library_target = _library_target
+ deps = [ ":$_library_target" ]
+ }
+ }
+
shared_library(_library_target) {
# Configs will always be defined since we set_defaults in BUILDCONFIG.gn.
configs = [] # Prevent list overwriting warning.
@@ -106,7 +119,7 @@
}
}
unittest_apk(_apk_target) {
- forward_variables_from(invoker, _apk_specific_vars + [ "deps" ])
+ forward_variables_from(invoker, _apk_specific_vars)
shared_library = ":$_library_target"
apk_name = invoker.target_name
if (defined(invoker.output_name)) {
@@ -114,74 +127,59 @@
install_script_name = "install_${invoker.output_name}"
}
- # TODO(agrieve): Remove this data_dep once bots don't build the _apk
- # target (post-GYP).
- # It's a bit backwards for the apk to depend on the runner script, since
- # the apk is conceptually a runtime_dep of the script. However, it is
- # currently necessary because the bots build this _apk target directly
- # rather than the group() below.
- data_deps = [
- ":$_test_runner_target",
- ]
- }
+ if (defined(invoker.deps)) {
+ deps = invoker.deps
+ } else {
+ deps = []
+ }
- # Incremental test targets work only for .apks.
- _incremental_test_runner_target =
- "${_output_name}_incremental__test_runner_script"
- test_runner_script(_incremental_test_runner_target) {
- forward_variables_from(invoker,
- _wrapper_script_vars + [
- "data",
- "data_deps",
- "deps",
- "public_deps",
- ])
- apk_target = ":$_apk_target"
- test_name = "${_output_name}_incremental"
- test_type = "gtest"
- test_suite = _output_name
- incremental_install = true
- }
- group("${target_name}_incremental") {
- testonly = true
- datadeps = [
- ":$_incremental_test_runner_target",
- ]
- deps = [
- ":${_apk_target}_incremental",
- ]
+ # Add the Java classes so that each target does not have to do it.
+ deps += [ "//base/test:test_support_java" ]
+
+ if (defined(_unwind_table_asset_name)) {
+ deps += [ ":${_unwind_table_asset_name}" ]
+ }
}
}
- _test_runner_target = "${_output_name}__test_runner_script"
test_runner_script(_test_runner_target) {
- forward_variables_from(invoker,
- _wrapper_script_vars + [
- "data",
- "data_deps",
- "deps",
- "public_deps",
- ])
+ forward_variables_from(invoker, _wrapper_script_vars)
if (_use_raw_android_executable) {
executable_dist_dir = "$root_out_dir/$_dist_target"
+ data_deps = [ ":$_exec_target" ]
} else {
apk_target = ":$_apk_target"
+ incremental_apk = incremental_install
+
+ # Dep needed for the test runner .runtime_deps file to pick up data
+ # deps from the forward_variables_from(invoker, "*") on the library.
+ data_deps = [ ":$_library_target" ]
}
test_name = _output_name
- test_type = "gtest"
test_suite = _output_name
+ test_type = "gtest"
}
- group(target_name) {
+ # Create a wrapper script rather than using a group() in order to ensure
+ # "ninja $target_name" always works. If this was a group(), then GN would
+ # not create a top-level alias for it if a target exists in another
+ # directory with the same $target_name.
+ # Also - bots run this script directly for "components_perftests".
+ generate_wrapper(target_name) {
testonly = true
- deps = [
- ":$_test_runner_target",
- ]
+ executable = "$root_build_dir/bin/run_$_output_name"
+ wrapper_script = "$root_build_dir/$_output_name"
+ deps = [ ":$_test_runner_target" ]
if (_use_raw_android_executable) {
deps += [ ":$_dist_target" ]
} else {
- deps += [ ":$_apk_target" ]
+ # Dep needed for the swarming .isolate file to pick up data
+ # deps from the forward_variables_from(invoker, "*") on the library.
+ deps += [
+ ":$_apk_target",
+ ":$_library_target",
+ ]
}
}
} else if (is_ios) {
@@ -192,19 +190,14 @@
bundle_data(_resources_bundle_data) {
visibility = [ ":$_test_target" ]
- sources = [
- "//testing/gtest_ios/Default.png",
- ]
- outputs = [
- "{{bundle_resources_dir}}/{{source_file_part}}",
- ]
+ sources = [ "//testing/gtest_ios/Default.png" ]
+ outputs = [ "{{bundle_resources_dir}}/{{source_file_part}}" ]
}
ios_app_bundle(_test_target) {
testonly = true
# See above call.
- set_sources_assignment_filter([])
forward_variables_from(invoker, "*", [ "testonly" ])
# Provide sensible defaults in case invoker did not define any of those
@@ -244,9 +237,7 @@
if (defined(invoker.output_name) && target_name != invoker.output_name) {
group("${invoker.output_name}_run") {
testonly = true
- deps = [
- ":${invoker.target_name}",
- ]
+ deps = [ ":${invoker.target_name}" ]
}
}
}
@@ -256,6 +247,8 @@
set_defaults("test") {
if (is_android) {
configs = default_shared_library_configs
+ configs -= [ "//build/config/android:hide_all_but_jni_onload" ]
+ configs += [ "//build/config/android:hide_all_but_jni" ]
} else {
configs = default_executable_configs
}
@@ -271,9 +264,7 @@
if (defined(invoker.configs)) {
configs += invoker.configs
}
- deps = [
- _pdfium_root_dir + ":pdfium_unittest_deps",
- ]
+ deps = [ _pdfium_root_dir + ":pdfium_unittest_deps" ]
if (defined(invoker.deps)) {
deps += invoker.deps
}
@@ -292,9 +283,7 @@
if (defined(invoker.configs)) {
configs += invoker.configs
}
- deps = [
- _pdfium_root_dir + ":pdfium_embeddertest_deps",
- ]
+ deps = [ _pdfium_root_dir + ":pdfium_embeddertest_deps" ]
if (defined(invoker.deps)) {
deps += invoker.deps
}
diff --git a/testing/test_fonts.cpp b/testing/test_fonts.cpp
new file mode 100644
index 0000000..e3b6559
--- /dev/null
+++ b/testing/test_fonts.cpp
@@ -0,0 +1,116 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/test_fonts.h"
+
+#include <set>
+#include <utility>
+
+#include "core/fxge/cfx_fontmapper.h"
+#include "core/fxge/cfx_fontmgr.h"
+#include "core/fxge/cfx_gemodule.h"
+#include "core/fxge/systemfontinfo_iface.h"
+#include "testing/utils/path_service.h"
+
+namespace {
+
+ByteString RenameFontForTesting(const ByteString& face) {
+ ByteString result;
+ if (face.Contains("Arial") || face.Contains("Calibri") ||
+ face.Contains("Helvetica")) {
+ // Sans
+ result = "Arimo";
+ } else if (face.IsEmpty() || face.Contains("Times")) {
+ // Serif
+ result = "Tinos";
+ } else if (face.Contains("Courier")) {
+ // Mono
+ result = "Cousine";
+ } else {
+ // Some tests expect the fallback font.
+ return face;
+ }
+
+ if (face.Contains("Bold"))
+ result += " Bold";
+
+ if (face.Contains("Italic") || face.Contains("Oblique"))
+ result += " Italic";
+
+ return result;
+}
+
+// Intercepts font requests and renames font faces to those in test_fonts.
+class SystemFontInfoWrapper : public SystemFontInfoIface {
+ public:
+ explicit SystemFontInfoWrapper(std::unique_ptr<SystemFontInfoIface> impl)
+ : impl_(std::move(impl)) {}
+ ~SystemFontInfoWrapper() { CHECK(active_fonts_.empty()); }
+
+ bool EnumFontList(CFX_FontMapper* pMapper) override {
+ return impl_->EnumFontList(pMapper);
+ }
+ void* MapFont(int weight,
+ bool bItalic,
+ FX_Charset charset,
+ int pitch_family,
+ const ByteString& face) override {
+ void* font = impl_->MapFont(weight, bItalic, charset, pitch_family,
+ RenameFontForTesting(face));
+ if (font) {
+ bool inserted = active_fonts_.insert(font).second;
+ CHECK(inserted);
+ }
+ return font;
+ }
+ void* GetFont(const ByteString& face) override {
+ return impl_->GetFont(RenameFontForTesting(face));
+ }
+ size_t GetFontData(void* hFont,
+ uint32_t table,
+ pdfium::span<uint8_t> buffer) override {
+ return impl_->GetFontData(hFont, table, buffer);
+ }
+ bool GetFaceName(void* hFont, ByteString* name) override {
+ auto face = RenameFontForTesting(*name);
+ return impl_->GetFaceName(hFont, &face);
+ }
+ bool GetFontCharset(void* hFont, FX_Charset* charset) override {
+ return impl_->GetFontCharset(hFont, charset);
+ }
+ void DeleteFont(void* hFont) override {
+ CHECK(active_fonts_.erase(hFont));
+ impl_->DeleteFont(hFont);
+ }
+
+ private:
+ std::unique_ptr<SystemFontInfoIface> impl_;
+ std::set<void*> active_fonts_;
+};
+
+} // namespace
+
+TestFonts::TestFonts() {
+ if (!PathService::GetExecutableDir(&font_path_))
+ return;
+ font_path_.push_back(PATH_SEPARATOR);
+ font_path_.append("test_fonts");
+ font_paths_ = std::make_unique<const char*[]>(2);
+ font_paths_[0] = font_path_.c_str();
+ font_paths_[1] = nullptr;
+}
+
+TestFonts::~TestFonts() = default;
+
+void TestFonts::InstallFontMapper() {
+ auto* font_mapper = CFX_GEModule::Get()->GetFontMgr()->GetBuiltinMapper();
+ font_mapper->SetSystemFontInfo(std::make_unique<SystemFontInfoWrapper>(
+ font_mapper->TakeSystemFontInfo()));
+}
+
+// static
+std::string TestFonts::RenameFont(const char* face) {
+ ByteString renamed_face = RenameFontForTesting(face);
+ return std::string(renamed_face.c_str());
+}
diff --git a/testing/test_fonts.h b/testing/test_fonts.h
new file mode 100644
index 0000000..4e2a822
--- /dev/null
+++ b/testing/test_fonts.h
@@ -0,0 +1,27 @@
+// Copyright 2021 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TESTING_TEST_FONTS_H_
+#define TESTING_TEST_FONTS_H_
+
+#include <memory>
+#include <string>
+
+class TestFonts {
+ public:
+ TestFonts();
+ ~TestFonts();
+
+ const char** font_paths() const { return font_paths_.get(); }
+
+ void InstallFontMapper();
+
+ static std::string RenameFont(const char* face);
+
+ private:
+ std::string font_path_;
+ std::unique_ptr<const char*[]> font_paths_;
+};
+
+#endif // TESTING_TEST_FONTS_H_
diff --git a/testing/test_loader.cpp b/testing/test_loader.cpp
index 33ee331..aac8429 100644
--- a/testing/test_loader.cpp
+++ b/testing/test_loader.cpp
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -6,7 +6,7 @@
#include <string.h>
-#include "third_party/base/logging.h"
+#include "third_party/base/notreached.h"
TestLoader::TestLoader(pdfium::span<const char> span) : m_Span(span) {}
diff --git a/testing/test_loader.h b/testing/test_loader.h
index 17ca9e9..f3d0042 100644
--- a/testing/test_loader.h
+++ b/testing/test_loader.h
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
diff --git a/testing/test_support.cpp b/testing/test_support.cpp
deleted file mode 100644
index 94ce528..0000000
--- a/testing/test_support.cpp
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2019 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 "testing/test_support.h"
-
-#include "core/fxge/cfx_gemodule.h"
-
-void InitializePDFTestEnvironment() {
- CFX_GEModule::Create(nullptr);
-}
-
-void DestroyPDFTestEnvironment() {
- CFX_GEModule::Destroy();
-}
diff --git a/testing/test_support.h b/testing/test_support.h
index f237ea5..1faf1a8 100644
--- a/testing/test_support.h
+++ b/testing/test_support.h
@@ -1,4 +1,4 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -46,7 +46,4 @@
} // namespace pdfium
-void InitializePDFTestEnvironment();
-void DestroyPDFTestEnvironment();
-
#endif // TESTING_TEST_SUPPORT_H_
diff --git a/testing/tools/.style.yapf b/testing/tools/.style.yapf
deleted file mode 100644
index de0c6a7..0000000
--- a/testing/tools/.style.yapf
+++ /dev/null
@@ -1,2 +0,0 @@
-[style]
-based_on_style = chromium
diff --git a/testing/tools/PRESUBMIT.py b/testing/tools/PRESUBMIT.py
index 59a3858..0b89656 100644
--- a/testing/tools/PRESUBMIT.py
+++ b/testing/tools/PRESUBMIT.py
@@ -1,16 +1,20 @@
-# Copyright 2019 The PDFium Authors. All rights reserved.
+# Copyright 2019 The PDFium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+
"""Presubmit script for PDFium testing tools.
See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
for more details on the presubmit API built into depot_tools.
"""
+USE_PYTHON3 = True
+
def _CommonChecks(input_api, output_api):
tests = []
- tests.extend(input_api.canned_checks.GetPylint(input_api, output_api))
+ tests.extend(
+ input_api.canned_checks.GetPylint(input_api, output_api, version='2.7'))
return tests
diff --git a/testing/tools/api_check.py b/testing/tools/api_check.py
index 66b3077..bea8845 100755
--- a/testing/tools/api_check.py
+++ b/testing/tools/api_check.py
@@ -1,5 +1,5 @@
-#!/usr/bin/env python
-# Copyright 2017 The PDFium Authors. All rights reserved.
+#!/usr/bin/env python3
+# Copyright 2017 The PDFium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Verifies exported functions in public/*.h are in fpdf_view_c_api_test.c.
@@ -99,16 +99,16 @@
def _FindDuplicates(functions):
- return set([f for f in functions if functions.count(f) > 1])
+ return {f for f in functions if functions.count(f) > 1}
def _CheckAndPrintFailures(failure_list, failure_message):
if not failure_list:
return True
- print '%s:' % failure_message
+ print('%s:' % failure_message)
for f in sorted(failure_list):
- print f
+ print(f)
return False
diff --git a/testing/tools/common.py b/testing/tools/common.py
index 108fcfd..80b7815 100755
--- a/testing/tools/common.py
+++ b/testing/tools/common.py
@@ -1,5 +1,5 @@
-#!/usr/bin/env python
-# Copyright 2015 The PDFium Authors. All rights reserved.
+#!/usr/bin/env python3
+# Copyright 2015 The PDFium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -10,6 +10,8 @@
import subprocess
import sys
+import pdfium_root
+
def os_name():
if sys.platform.startswith('linux'):
@@ -21,14 +23,6 @@
raise Exception('Confused, can not determine OS, aborting.')
-def RunCommand(cmd):
- try:
- subprocess.check_call(cmd)
- return None
- except subprocess.CalledProcessError as e:
- return e
-
-
def RunCommandPropagateErr(cmd,
stdout_has_errors=False,
exit_status_on_error=None):
@@ -61,50 +55,17 @@
return output
-# RunCommandExtractHashedFiles returns a tuple: (raised_exception, hashed_files)
-# It runs the given command. If it fails it will return an exception and None.
-# If it succeeds it will return None and the list of processed files extracted
-# from the output of the command. It expects lines in this format:
-# MD5:<path_to_image_file>:<md5_hash_in_hex>
-# The returned hashed_files is a list of (file_path, MD5-hash) pairs.
-def RunCommandExtractHashedFiles(cmd):
- try:
- output = subprocess.check_output(cmd, universal_newlines=True)
- ret = []
- for line in output.split('\n'):
- line = line.strip()
- if line.startswith("MD5:"):
- ret.append([x.strip() for x in line.lstrip("MD5:").rsplit(":", 1)])
- return None, ret
- except subprocess.CalledProcessError as e:
- return e, None
-
-
class DirectoryFinder:
'''A class for finding directories and paths under either a standalone
checkout or a chromium checkout of PDFium.'''
def __init__(self, build_location):
- # |build_location| is typically "out/Debug" or "out/Release".
- # Expect |my_dir| to be .../pdfium/testing/tools.
- self.my_dir = os.path.dirname(os.path.realpath(__file__))
- self.testing_dir = os.path.dirname(self.my_dir)
- if (os.path.basename(self.my_dir) != 'tools' or
- os.path.basename(self.testing_dir) != 'testing'):
- raise Exception('Confused, can not find pdfium root directory, aborting.')
- self.pdfium_dir = os.path.dirname(self.testing_dir)
- # Find path to build directory. This depends on whether this is a
- # standalone build vs. a build as part of a chromium checkout. For
- # standalone, we expect a path like .../pdfium/out/Debug, but for
- # chromium, we expect a path like .../src/out/Debug two levels
- # higher (to skip over the third_party/pdfium path component under
- # which chromium sticks pdfium).
- self.base_dir = self.pdfium_dir
- one_up_dir = os.path.dirname(self.base_dir)
- two_up_dir = os.path.dirname(one_up_dir)
- if (os.path.basename(two_up_dir) == 'src' and
- os.path.basename(one_up_dir) == 'third_party'):
- self.base_dir = two_up_dir
+ # `build_location` is typically "out/Debug" or "out/Release".
+ root_finder = pdfium_root.RootDirectoryFinder()
+ self.testing_dir = os.path.join(root_finder.pdfium_root, 'testing')
+ self.my_dir = os.path.join(self.testing_dir, 'tools')
+ self.pdfium_dir = root_finder.pdfium_root
+ self.base_dir = root_finder.source_root
self.build_dir = os.path.join(self.base_dir, build_location)
self.os_name = os_name()
@@ -133,26 +94,31 @@
result = os.path.join(result, other_components)
return result
+ def ThirdPartyFontsDir(self):
+ '''Finds directory with the test fonts.'''
+ return os.path.join(self.base_dir, 'third_party', 'test_fonts')
+
def GetBooleanGnArg(arg_name, build_dir, verbose=False):
'''Extract the value of a boolean flag in args.gn'''
cwd = os.getcwd()
os.chdir(build_dir)
gn_args_output = subprocess.check_output(
- ['gn', 'args', '.', '--list=%s' % arg_name, '--short'])
+ ['gn', 'args', '.', '--list=%s' % arg_name, '--short']).decode('utf8')
os.chdir(cwd)
arg_match_output = re.search('%s = (.*)' % arg_name, gn_args_output).group(1)
if verbose:
- print >> sys.stderr, "Found '%s' for value of %s" % (arg_match_output,
- arg_name)
+ print(
+ "Found '%s' for value of %s" % (arg_match_output, arg_name),
+ file=sys.stderr)
return arg_match_output == 'true'
def PrintWithTime(s):
"""Prints s prepended by a timestamp."""
- print '[%s] %s' % (datetime.datetime.now().strftime("%Y%m%d %H:%M:%S"), s)
+ print('[%s] %s' % (datetime.datetime.now().strftime("%Y%m%d %H:%M:%S"), s))
def PrintErr(s):
"""Prints s to stderr."""
- print >> sys.stderr, s
+ print(s, file=sys.stderr)
diff --git a/testing/tools/coverage/coverage_report.py b/testing/tools/coverage/coverage_report.py
index 5785eab..2c57946 100755
--- a/testing/tools/coverage/coverage_report.py
+++ b/testing/tools/coverage/coverage_report.py
@@ -1,5 +1,5 @@
-#!/usr/bin/env python
-# Copyright 2017 The PDFium Authors. All rights reserved.
+#!/usr/bin/env vpython3
+# Copyright 2017 The PDFium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Generates a coverage report for given tests.
@@ -16,14 +16,11 @@
import subprocess
import sys
-# Add src dir to path to avoid having to set PYTHONPATH.
+# Add parent dir to avoid having to set PYTHONPATH.
sys.path.append(
- os.path.abspath(
- os.path.join(
- os.path.dirname(__file__), os.path.pardir, os.path.pardir,
- os.path.pardir)))
+ os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
-from testing.tools.common import GetBooleanGnArg
+import common
# 'binary' is the file that is to be run for the test.
# 'use_test_runner' indicates if 'binary' depends on test_runner.py and thus
@@ -41,18 +38,32 @@
TestSpec('run_corpus_tests.py', True, []),
'corpus_tests_javascript_disabled':
TestSpec('run_corpus_tests.py', True, ['--disable-javascript']),
+ 'corpus_tests_xfa_disabled':
+ TestSpec('run_corpus_tests.py', True, ['--disable-xfa']),
+ 'corpus_tests_render_oneshot':
+ TestSpec('run_corpus_tests.py', True, ['--render-oneshot']),
+ 'corpus_tests_reverse_byte_order':
+ TestSpec('run_corpus_tests.py', True, ['--reverse-byte-order']),
'javascript_tests':
TestSpec('run_javascript_tests.py', True, []),
'javascript_tests_javascript_disabled':
TestSpec('run_javascript_tests.py', True, ['--disable-javascript']),
+ 'javascript_tests_xfa_disabled':
+ TestSpec('run_javascript_tests.py', True, ['--disable-xfa']),
'pixel_tests':
TestSpec('run_pixel_tests.py', True, []),
'pixel_tests_javascript_disabled':
TestSpec('run_pixel_tests.py', True, ['--disable-javascript']),
+ 'pixel_tests_xfa_disabled':
+ TestSpec('run_pixel_tests.py', True, ['--disable-xfa']),
+ 'pixel_tests_render_oneshot':
+ TestSpec('run_pixel_tests.py', True, ['--render-oneshot']),
+ 'pixel_tests_reverse_byte_order':
+ TestSpec('run_pixel_tests.py', True, ['--reverse-byte-order']),
}
-class CoverageExecutor(object):
+class CoverageExecutor:
def __init__(self, parser, args):
"""Initialize executor based on the current script environment
@@ -85,14 +96,14 @@
'No valid tests in set to be run. This is likely due to bad command '
'line arguments')
- if not GetBooleanGnArg('use_clang_coverage', self.build_directory,
- self.verbose):
+ if not common.GetBooleanGnArg('use_clang_coverage', self.build_directory,
+ self.verbose):
parser.error(
'use_clang_coverage does not appear to be set to true for build, but '
'is needed')
- self.use_goma = GetBooleanGnArg('use_goma', self.build_directory,
- self.verbose)
+ self.use_goma = common.GetBooleanGnArg('use_goma', self.build_directory,
+ self.verbose)
self.output_directory = args['output_directory']
if not os.path.exists(self.output_directory):
@@ -109,38 +120,38 @@
def check_output(self, args, dry_run=False, env=None):
"""Dry run aware wrapper of subprocess.check_output()"""
if dry_run:
- print "Would have run '%s'" % ' '.join(args)
+ print("Would have run '%s'" % ' '.join(args))
return ''
output = subprocess.check_output(args, env=env)
if self.verbose:
- print "check_output(%s) returned '%s'" % (args, output)
+ print("check_output(%s) returned '%s'" % (args, output))
return output
def call(self, args, dry_run=False, env=None):
"""Dry run aware wrapper of subprocess.call()"""
if dry_run:
- print "Would have run '%s'" % ' '.join(args)
+ print("Would have run '%s'" % ' '.join(args))
return 0
output = subprocess.call(args, env=env)
if self.verbose:
- print 'call(%s) returned %s' % (args, output)
+ print('call(%s) returned %s' % (args, output))
return output
def call_silent(self, args, dry_run=False, env=None):
"""Dry run aware wrapper of subprocess.call() that eats output from call"""
if dry_run:
- print "Would have run '%s'" % ' '.join(args)
+ print("Would have run '%s'" % ' '.join(args))
return 0
with open(os.devnull, 'w') as f:
output = subprocess.call(args, env=env, stdout=f)
if self.verbose:
- print 'call_silent(%s) returned %s' % (args, output)
+ print('call_silent(%s) returned %s' % (args, output))
return output
def calculate_coverage_tests(self, args):
@@ -186,10 +197,10 @@
spec: Tuple containing the TestSpec.
"""
if self.verbose:
- print "Generating coverage for test '%s', using data '%s'" % (name, spec)
+ print("Generating coverage for test '%s', using data '%s'" % (name, spec))
if not os.path.exists(spec.binary):
print('Unable to generate coverage for %s, since it appears to not exist'
- ' @ %s') % (name, spec.binary)
+ ' @ %s' % (name, spec.binary))
return False
binary_args = [spec.binary]
@@ -212,7 +223,7 @@
binary_args.extend(['-j', '8', '--build-dir', self.build_directory])
if self.call(binary_args, dry_run=self.dry_run, env=env) and self.verbose:
print('Running %s appears to have failed, which might affect '
- 'results') % spec.binary
+ 'results' % spec.binary)
return True
@@ -241,7 +252,7 @@
coverage_args += ['-o', report_directory]
coverage_args += self.build_targets
- # Whitelist the directories of interest
+ # Only analyze the directories of interest.
coverage_args += ['-f', 'core']
coverage_args += ['-f', 'fpdfsdk']
coverage_args += ['-f', 'fxbarcode']
@@ -250,7 +261,7 @@
coverage_args += ['-f', 'samples']
coverage_args += ['-f', 'xfa']
- # Blacklist test files
+ # Ignore test files.
coverage_args += ['-i', '.*test.*']
# Component view is only useful for Chromium
@@ -261,24 +272,24 @@
def run(self):
"""Setup environment, execute the tests and generate coverage report"""
if not self.fetch_profiling_tools():
- print 'Unable to fetch profiling tools'
+ print('Unable to fetch profiling tools')
return False
if not self.build_binaries():
- print 'Failed to successfully build binaries'
+ print('Failed to successfully build binaries')
return False
- for name in self.coverage_tests.keys():
+ for name in self.coverage_tests:
if not self.generate_coverage(name, self.coverage_tests[name]):
- print 'Failed to successfully generate coverage data'
+ print('Failed to successfully generate coverage data')
return False
if not self.merge_raw_coverage_results():
- print 'Failed to successfully merge raw coverage results'
+ print('Failed to successfully merge raw coverage results')
return False
if not self.generate_html_report():
- print 'Failed to successfully generate HTML report'
+ print('Failed to successfully generate HTML report')
return False
return True
diff --git a/testing/tools/encode_pdf_filter.py b/testing/tools/encode_pdf_filter.py
index 2d56543..1985fb7 100755
--- a/testing/tools/encode_pdf_filter.py
+++ b/testing/tools/encode_pdf_filter.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# Copyright 2019 The PDFium Authors. All rights reserved.
+# Copyright 2019 The PDFium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Encodes binary data using one or more PDF stream filters.
@@ -13,6 +13,7 @@
"""
import argparse
+from abc import ABCMeta, abstractmethod
import base64
import collections
import collections.abc
@@ -21,10 +22,22 @@
import zlib
-class _PdfStream:
+class _PdfStream(metaclass=ABCMeta):
_unique_filter_classes = []
_filter_classes = {}
+ @classmethod
+ @property
+ @abstractmethod
+ def name(cls):
+ pass
+
+ @classmethod
+ @property
+ @abstractmethod
+ def aliases(cls):
+ pass
+
@staticmethod
def GetFilterByName(name):
# Tolerate any case-insensitive match for "/Name" or "Name", or an alias.
@@ -38,18 +51,24 @@
return filter_class
- @staticmethod
- def RegisterFilter(filter_class):
- assert filter_class not in _PdfStream._unique_filter_classes
- _PdfStream._unique_filter_classes.append(filter_class)
+ @classmethod
+ def Register(cls):
+ assert cls not in _PdfStream._unique_filter_classes
+ _PdfStream._unique_filter_classes.append(cls)
+ cls.RegisterByName()
+ cls.RegisterByAliases()
- assert filter_class.name[0] == '/'
- lower_name = filter_class.name.lower()
- _PdfStream._filter_classes[lower_name] = filter_class
- _PdfStream._filter_classes[lower_name[1:]] = filter_class
+ @classmethod
+ def RegisterByName(cls):
+ assert cls.name[0] == '/'
+ lower_name = cls.name.lower()
+ _PdfStream._filter_classes[lower_name] = cls
+ _PdfStream._filter_classes[lower_name[1:]] = cls
- for alias in filter_class.aliases:
- _PdfStream._filter_classes[alias.lower()] = filter_class
+ @classmethod
+ def RegisterByAliases(cls):
+ for alias in cls.aliases:
+ _PdfStream._filter_classes[alias.lower()] = cls
@staticmethod
def GetHelp():
@@ -59,6 +78,21 @@
', '.join(filter_class.aliases))
return text
+ @classmethod
+ def AddEntries(cls, entries):
+ _PdfStream.AddListEntry(entries, 'Filter', cls.name)
+
+ @staticmethod
+ def AddListEntry(entries, key, value):
+ old_value = entries.get(key)
+ if old_value is None:
+ entries[key] = value
+ else:
+ if not isinstance(old_value, collections.abc.MutableSequence):
+ old_value = [old_value]
+ entries[key] = old_value
+ old_value.append(value)
+
def __init__(self, out_buffer, **kwargs):
del kwargs
self.buffer = out_buffer
@@ -78,6 +112,22 @@
def __init__(self):
super().__init__(io.BytesIO())
+ @classmethod
+ @property
+ def name(cls):
+ # Return an invalid name, so as to ensure _SinkPdfStream.Register()
+ # cannot be called. This method has to be implemented, because this
+ # script create `_SinkPdfStream` instances.
+ return ''
+
+ @classmethod
+ @property
+ def aliases(cls):
+ # Return an invalid aliases, so as to ensure _SinkPdfStream.Register()
+ # cannot be called. This method has to be implemented, because this
+ # script create `_SinkPdfStream` instances.
+ return ()
+
def close(self):
# Don't call io.BytesIO.close(); this deallocates the written data.
self.flush()
@@ -93,6 +143,18 @@
self.wrapcol = wrapcol
self.column = 0
+ @classmethod
+ @property
+ @abstractmethod
+ def name(cls):
+ pass
+
+ @classmethod
+ @property
+ @abstractmethod
+ def aliases(cls):
+ pass
+
def write(self, data):
if not self.wrapcol:
self.buffer.write(data)
@@ -113,8 +175,18 @@
class _Ascii85DecodePdfStream(_AsciiPdfStream):
- name = '/ASCII85Decode'
- aliases = ('ascii85', 'base85')
+ _name = '/ASCII85Decode'
+ _aliases = ('ascii85', 'base85')
+
+ @classmethod
+ @property
+ def name(cls):
+ return cls._name
+
+ @classmethod
+ @property
+ def aliases(cls):
+ return cls._aliases
def __init__(self, out_buffer, **kwargs):
super().__init__(out_buffer, **kwargs)
@@ -137,8 +209,18 @@
class _AsciiHexDecodePdfStream(_AsciiPdfStream):
- name = '/ASCIIHexDecode'
- aliases = ('base16', 'hex', 'hexadecimal')
+ _name = '/ASCIIHexDecode'
+ _aliases = ('base16', 'hex', 'hexadecimal')
+
+ @classmethod
+ @property
+ def name(cls):
+ return cls._name
+
+ @classmethod
+ @property
+ def aliases(cls):
+ return cls._aliases
def __init__(self, out_buffer, **kwargs):
super().__init__(out_buffer, **kwargs)
@@ -148,13 +230,23 @@
class _FlateDecodePdfStream(_PdfStream):
- name = '/FlateDecode'
- aliases = ('deflate', 'flate', 'zlib')
+ _name = '/FlateDecode'
+ _aliases = ('deflate', 'flate', 'zlib')
def __init__(self, out_buffer, **kwargs):
super().__init__(out_buffer, **kwargs)
self.deflate = zlib.compressobj(level=9, memLevel=9)
+ @classmethod
+ @property
+ def name(cls):
+ return cls._name
+
+ @classmethod
+ @property
+ def aliases(cls):
+ return cls._aliases
+
def write(self, data):
self.buffer.write(self.deflate.compress(data))
@@ -166,9 +258,126 @@
self.buffer.close()
-_PdfStream.RegisterFilter(_Ascii85DecodePdfStream)
-_PdfStream.RegisterFilter(_AsciiHexDecodePdfStream)
-_PdfStream.RegisterFilter(_FlateDecodePdfStream)
+class _VirtualPdfStream(_PdfStream):
+
+ @classmethod
+ @property
+ @abstractmethod
+ def name(cls):
+ pass
+
+ @classmethod
+ @property
+ @abstractmethod
+ def aliases(cls):
+ pass
+
+ @classmethod
+ def RegisterByName(cls):
+ pass
+
+ @classmethod
+ def AddEntries(cls, entries):
+ pass
+
+
+class _PassthroughPdfStream(_VirtualPdfStream):
+ _name = '(virtual) passthrough'
+ _aliases = ('noop', 'passthrough')
+
+ @classmethod
+ @property
+ def name(cls):
+ return cls._name
+
+ @classmethod
+ @property
+ def aliases(cls):
+ return cls._aliases
+
+
+class _PngIdatPdfStream(_VirtualPdfStream):
+ _name = '(virtual) PNG IDAT'
+ _aliases = ('png',)
+
+ _EXPECT_HEADER = -1
+ _EXPECT_LENGTH = -2
+ _EXPECT_CHUNK_TYPE = -3
+ _EXPECT_CRC = -4
+
+ _PNG_HEADER = 0x89504E470D0A1A0A
+ _PNG_CHUNK_IDAT = 0x49444154
+
+ @classmethod
+ @property
+ def name(cls):
+ return cls._name
+
+ @classmethod
+ @property
+ def aliases(cls):
+ return cls._aliases
+
+ @classmethod
+ def AddEntries(cls, entries):
+ # Technically only true for compression method 0 (zlib), but no other
+ # methods have been standardized.
+ _PdfStream.AddListEntry(entries, 'Filter', '/FlateDecode')
+
+ def __init__(self, out_buffer, **kwargs):
+ super().__init__(out_buffer, **kwargs)
+ self.chunk = _PngIdatPdfStream._EXPECT_HEADER
+ self.remaining = 8
+ self.accumulator = 0
+ self.length = 0
+
+ def write(self, data):
+ position = 0
+ while position < len(data):
+ if self.chunk >= 0:
+ # Only pass through IDAT chunk data.
+ read_size = min(self.remaining, len(data) - position)
+ if self.chunk == _PngIdatPdfStream._PNG_CHUNK_IDAT:
+ self.buffer.write(data[position:position + read_size])
+ self.remaining -= read_size
+ if self.remaining == 0:
+ self.ResetAccumulator(_PngIdatPdfStream._EXPECT_CRC, 4)
+ position += read_size
+ else:
+ # As far as we're concerned, PNG files are just a header followed by a
+ # series of (length, chunk type, data[length], CRC) chunks.
+ if self.AccumulateByte(data[position]):
+ if self.chunk == _PngIdatPdfStream._EXPECT_HEADER:
+ if self.accumulator != _PngIdatPdfStream._PNG_HEADER:
+ raise ValueError('Invalid PNG header', self.accumulator)
+ self.ResetAccumulator(_PngIdatPdfStream._EXPECT_LENGTH, 4)
+ elif self.chunk == _PngIdatPdfStream._EXPECT_LENGTH:
+ self.length = self.accumulator
+ self.ResetAccumulator(_PngIdatPdfStream._EXPECT_CHUNK_TYPE, 4)
+ elif self.chunk == _PngIdatPdfStream._EXPECT_CHUNK_TYPE:
+ self.ResetAccumulator(self.accumulator, self.length)
+ elif self.chunk == _PngIdatPdfStream._EXPECT_CRC:
+ # Don't care if the CRC is correct.
+ self.ResetAccumulator(_PngIdatPdfStream._EXPECT_LENGTH, 4)
+ position += 1
+
+ def ResetAccumulator(self, chunk, remaining):
+ self.chunk = chunk
+ self.remaining = remaining
+ self.accumulator = 0
+
+ def AccumulateByte(self, byte):
+ assert self.remaining > 0
+ self.accumulator = self.accumulator << 8 | byte
+ self.remaining -= 1
+ return self.remaining == 0
+
+
+_Ascii85DecodePdfStream.Register()
+_AsciiHexDecodePdfStream.Register()
+_FlateDecodePdfStream.Register()
+_PassthroughPdfStream.Register()
+_PngIdatPdfStream.Register()
_DEFAULT_FILTERS = (_Ascii85DecodePdfStream, _FlateDecodePdfStream)
@@ -261,7 +470,7 @@
def _EncodePdfValue(value):
- if isinstance(value, collections.abc.Sequence):
+ if isinstance(value, collections.abc.MutableSequence):
value = '[' + ' '.join(value) + ']'
return value
@@ -276,7 +485,8 @@
out_buffer.close()
entries = collections.OrderedDict()
- entries['Filter'] = [f.name for f in args.filter]
+ for f in args.filter:
+ f.AddEntries(entries)
_WritePdfStreamObject(
args.outfile.buffer,
data=encoded_sink.getbuffer(),
diff --git a/testing/tools/fixup_pdf_template.py b/testing/tools/fixup_pdf_template.py
index ee47c4b..da2d608 100755
--- a/testing/tools/fixup_pdf_template.py
+++ b/testing/tools/fixup_pdf_template.py
@@ -1,5 +1,5 @@
-#!/usr/bin/env python
-# Copyright 2014 The PDFium Authors. All rights reserved.
+#!/usr/bin/env python3
+# Copyright 2014 The PDFium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Expands a hand-written PDF testcase (template) into a valid PDF file.
@@ -11,17 +11,32 @@
{{header}} - expands to the header comment required for PDF files.
{{xref}} - expands to a generated xref table, noting the offset.
{{trailer}} - expands to a standard trailer with "1 0 R" as the /Root.
+ {{trailersize}} - expands to `/Size n`, to be used in non-standard trailers.
{{startxref} - expands to a startxref directive followed by correct offset.
- {{object x y}} - expands to |x y obj| declaration, noting the offset.
- {{streamlen}} - expands to |/Length n|.
+ {{startxrefobj x y} - expands to a startxref directive followed by correct
+ offset pointing to the start of `x y obj`.
+ {{object x y}} - expands to `x y obj` declaration, noting the offset.
+ {{streamlen}} - expands to `/Length n`.
"""
-import cStringIO
+import io
import optparse
import os
import re
import sys
+# Line Endings.
+WINDOWS_LINE_ENDING = b'\r\n'
+UNIX_LINE_ENDING = b'\n'
+
+# List of extensions whose line endings should be modified after parsing.
+EXTENSION_OVERRIDE_LINE_ENDINGS = [
+ '.js',
+ '.fragment',
+ '.in',
+ '.xml',
+]
+
class StreamLenState:
START = 1
@@ -30,28 +45,33 @@
class TemplateProcessor:
- HEADER_TOKEN = '{{header}}'
- HEADER_REPLACEMENT = '%PDF-1.7\n%\xa0\xf2\xa4\xf4'
+ HEADER_TOKEN = b'{{header}}'
+ HEADER_REPLACEMENT = b'%PDF-1.7\n%\xa0\xf2\xa4\xf4'
- XREF_TOKEN = '{{xref}}'
- XREF_REPLACEMENT = 'xref\n%d %d\n'
+ XREF_TOKEN = b'{{xref}}'
+ XREF_REPLACEMENT = b'xref\n%d %d\n'
- XREF_REPLACEMENT_N = '%010d %05d n \n'
- XREF_REPLACEMENT_F = '0000000000 65535 f \n'
+ XREF_REPLACEMENT_N = b'%010d %05d n \n'
+ XREF_REPLACEMENT_F = b'0000000000 65535 f \n'
# XREF rows must be exactly 20 bytes - space required.
assert len(XREF_REPLACEMENT_F) == 20
- TRAILER_TOKEN = '{{trailer}}'
- TRAILER_REPLACEMENT = 'trailer <<\n /Root 1 0 R\n /Size %d\n>>'
+ TRAILER_TOKEN = b'{{trailer}}'
+ TRAILER_REPLACEMENT = b'trailer <<\n /Root 1 0 R\n /Size %d\n>>'
- STARTXREF_TOKEN = '{{startxref}}'
- STARTXREF_REPLACEMENT = 'startxref\n%d'
+ TRAILERSIZE_TOKEN = b'{{trailersize}}'
+ TRAILERSIZE_REPLACEMENT = b'/Size %d'
- OBJECT_PATTERN = r'\{\{object\s+(\d+)\s+(\d+)\}\}'
- OBJECT_REPLACEMENT = r'\1 \2 obj'
+ STARTXREF_TOKEN = b'{{startxref}}'
+ STARTXREF_REPLACEMENT = b'startxref\n%d'
- STREAMLEN_TOKEN = '{{streamlen}}'
- STREAMLEN_REPLACEMENT = '/Length %d'
+ STARTXREFOBJ_PATTERN = b'\{\{startxrefobj\s+(\d+)\s+(\d+)\}\}'
+
+ OBJECT_PATTERN = b'\{\{object\s+(\d+)\s+(\d+)\}\}'
+ OBJECT_REPLACEMENT = b'\g<1> \g<2> obj'
+
+ STREAMLEN_TOKEN = b'{{streamlen}}'
+ STREAMLEN_REPLACEMENT = b'/Length %d'
def __init__(self):
self.streamlen_state = StreamLenState.START
@@ -82,12 +102,12 @@
return
if (self.streamlen_state == StreamLenState.FIND_STREAM and
- line.rstrip() == 'stream'):
+ line.rstrip() == b'stream'):
self.streamlen_state = StreamLenState.FIND_ENDSTREAM
return
if self.streamlen_state == StreamLenState.FIND_ENDSTREAM:
- if line.rstrip() == 'endstream':
+ if line.rstrip() == b'endstream':
self.streamlen_state = StreamLenState.START
else:
self.streamlens[-1] += len(line)
@@ -104,6 +124,9 @@
if self.TRAILER_TOKEN in line:
replacement = self.TRAILER_REPLACEMENT % (self.max_object_number + 1)
line = line.replace(self.TRAILER_TOKEN, replacement)
+ if self.TRAILERSIZE_TOKEN in line:
+ replacement = self.TRAILERSIZE_REPLACEMENT % (self.max_object_number + 1)
+ line = line.replace(self.TRAILERSIZE_TOKEN, replacement)
if self.STARTXREF_TOKEN in line:
replacement = self.STARTXREF_REPLACEMENT % self.xref_offset
line = line.replace(self.STARTXREF_TOKEN, replacement)
@@ -111,6 +134,12 @@
if match:
self.insert_xref_entry(int(match.group(1)), int(match.group(2)))
line = re.sub(self.OBJECT_PATTERN, self.OBJECT_REPLACEMENT, line)
+ match = re.match(self.STARTXREFOBJ_PATTERN, line)
+ if match:
+ (offset, generation_number) = self.objects[int(match.group(1))]
+ assert int(match.group(2)) == generation_number
+ replacement = self.STARTXREF_REPLACEMENT % offset
+ line = re.sub(self.STARTXREFOBJ_PATTERN, replacement, line)
self.offset += len(line)
return line
@@ -119,7 +148,7 @@
processor = TemplateProcessor()
try:
with open(output_path, 'wb') as outfile:
- preprocessed = cStringIO.StringIO()
+ preprocessed = io.BytesIO()
for line in infile:
preprocessed.write(line)
processor.preprocess_line(line)
@@ -127,27 +156,43 @@
for line in preprocessed:
outfile.write(processor.process_line(line))
except IOError:
- print >> sys.stderr, 'failed to process %s' % input_path
+ print('failed to process %s' % input_path, file=sys.stderr)
def insert_includes(input_path, output_file, visited_set):
input_path = os.path.normpath(input_path)
if input_path in visited_set:
- print >> sys.stderr, 'Circular inclusion %s, ignoring' % input_path
+ print('Circular inclusion %s, ignoring' % input_path, file=sys.stderr)
return
visited_set.add(input_path)
try:
+ _, file_extension = os.path.splitext(input_path)
+ override_line_endings = (file_extension in EXTENSION_OVERRIDE_LINE_ENDINGS)
+
+ end_of_file_line_ending = False
with open(input_path, 'rb') as infile:
for line in infile:
- match = re.match(r'\s*\{\{include\s+(.+)\}\}', line)
+ match = re.match(b'\s*\{\{include\s+(.+)\}\}', line)
if match:
insert_includes(
- os.path.join(os.path.dirname(input_path), match.group(1)),
- output_file, visited_set)
+ os.path.join(
+ os.path.dirname(input_path),
+ match.group(1).decode('utf-8')), output_file, visited_set)
else:
+ if override_line_endings:
+ # Replace CRLF with LF line endings for .in files.
+ if line.endswith(WINDOWS_LINE_ENDING):
+ line = line.removesuffix(WINDOWS_LINE_ENDING) + UNIX_LINE_ENDING
+ end_of_file_line_ending = True
+ else:
+ end_of_file_line_ending = line.endswith(UNIX_LINE_ENDING)
output_file.write(line)
+
+ # Ensure the include ends on its own line.
+ if not end_of_file_line_ending:
+ output_file.write(UNIX_LINE_ENDING)
except IOError:
- print >> sys.stderr, 'failed to include %s' % input_path
+ print('failed to include %s' % input_path, file=sys.stderr)
raise
visited_set.discard(input_path)
@@ -162,7 +207,7 @@
output_dir = os.path.dirname(testcase_path)
if options.output_dir:
output_dir = options.output_dir
- intermediate_stream = cStringIO.StringIO()
+ intermediate_stream = io.BytesIO()
insert_includes(testcase_path, intermediate_stream, set())
intermediate_stream.seek(0)
output_path = os.path.join(output_dir, testcase_root + '.pdf')
diff --git a/testing/tools/githelper.py b/testing/tools/githelper.py
index 91a6c71..19071b0 100644
--- a/testing/tools/githelper.py
+++ b/testing/tools/githelper.py
@@ -1,15 +1,14 @@
-# Copyright 2017 The PDFium Authors. All rights reserved.
+# Copyright 2017 The PDFium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Classes for dealing with git."""
import subprocess
-# pylint: disable=relative-import
from common import RunCommandPropagateErr
-class GitHelper(object):
+class GitHelper:
"""Issues git commands. Stateful."""
def __init__(self):
@@ -20,8 +19,8 @@
RunCommandPropagateErr(['git', 'checkout', branch], exit_status_on_error=1)
def FetchOriginMaster(self):
- """Fetches new changes on origin/master."""
- RunCommandPropagateErr(['git', 'fetch', 'origin', 'master'],
+ """Fetches new changes on origin/main."""
+ RunCommandPropagateErr(['git', 'fetch', 'origin', 'main'],
exit_status_on_error=1)
def StashPush(self):
diff --git a/testing/tools/gold.py b/testing/tools/gold.py
deleted file mode 100644
index c686bba..0000000
--- a/testing/tools/gold.py
+++ /dev/null
@@ -1,284 +0,0 @@
-# Copyright 2015 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.
-
-import json
-import os
-import shlex
-import shutil
-import ssl
-import urllib2
-
-
-def _ParseKeyValuePairs(kv_str):
- """
- Parses a string of the type 'key1 value1 key2 value2' into a dict.
- """
- kv_pairs = shlex.split(kv_str)
- if len(kv_pairs) % 2:
- raise ValueError('Uneven number of key/value pairs. Got %s' % kv_str)
- return {kv_pairs[i]: kv_pairs[i + 1] for i in xrange(0, len(kv_pairs), 2)}
-
-
-# This module downloads a json provided by Skia Gold with the expected baselines
-# for each test file.
-#
-# The expected format for the json is:
-# {
-# "commit": {
-# "author": "John Doe (jdoe@chromium.org)",
-# "commit_time": 1510598123,
-# "hash": "cee39e6e90c219cc91f2c94a912a06977f4461a0"
-# },
-# "master": {
-# "abc.pdf.1": {
-# "0ec3d86f545052acd7c9a16fde8ca9d4": 1,
-# "80455b71673becc9fbc100d6da56ca65": 1,
-# "b68e2ecb80090b4502ec89ad1be2322c": 1
-# },
-# "defgh.pdf.0": {
-# "01e020cd4cd05c6738e479a46a506044": 1,
-# "b68e2ecb80090b4502ec89ad1be2322c": 1
-# }
-# },
-# "changeLists": {
-# "18499" : {
-# "abc.pdf.1": {
-# "d5dd649124cf1779152253dc8fb239c5": 1,
-# "42a270581930579cdb0f28674972fb1a": 1,
-# }
-# }
-# }
-# }
-class GoldBaseline(object):
-
- def __init__(self, properties_str):
- """
- properties_str is a string with space separated key/value pairs that
- is used to find the cl number for which to baseline
- """
- self._properties = _ParseKeyValuePairs(properties_str)
- self._baselines = self._LoadSkiaGoldBaselines()
-
- def _LoadSkiaGoldBaselines(self):
- """
- Download the baseline json and return a list of the two baselines that
- should be used to match hashes (master and cl#).
- """
- GOLD_BASELINE_URL = 'https://pdfium-gold.skia.org/json/baseline'
-
- # If we have an issue number add it to the baseline URL
- cl_number_str = self._properties.get('issue', None)
- url = GOLD_BASELINE_URL + ('/' + cl_number_str if cl_number_str else '')
-
- json_data = ''
- MAX_TIMEOUT = 33 # 5 tries. (2, 4, 8, 16, 32)
- timeout = 2
- while True:
- try:
- response = urllib2.urlopen(url, timeout=timeout)
- c_type = response.headers.get('Content-type', '')
- EXPECTED_CONTENT_TYPE = 'application/json'
- if c_type != EXPECTED_CONTENT_TYPE:
- raise ValueError('Invalid content type. Got %s instead of %s' %
- (c_type, EXPECTED_CONTENT_TYPE))
- json_data = response.read()
- break # If this line is reached, then no exception occurred.
- except (ssl.SSLError, urllib2.HTTPError, urllib2.URLError) as e:
- timeout *= 2
- if timeout < MAX_TIMEOUT:
- continue
- print('Error: Unable to read skia gold json from %s: %s' % (url, e))
- return None
-
- try:
- data = json.loads(json_data)
- except ValueError as e:
- print 'Error: Malformed json read from %s: %s' % (url, e)
- return None
-
- return data.get('master', {})
-
- # Return values for MatchLocalResult().
- MATCH = 'match'
- MISMATCH = 'mismatch'
- NO_BASELINE = 'no_baseline'
- BASELINE_DOWNLOAD_FAILED = 'baseline_download_failed'
-
- def MatchLocalResult(self, test_name, md5_hash):
- """
- Match a locally generated hash of a test cases rendered image with the
- expected hashes downloaded in the baselines json.
-
- Each baseline is a dict mapping the test case name to a dict with the
- expected hashes as keys. Therefore, this list of baselines should be
- searched until the test case name is found, then the hash should be matched
- with the options in that dict. If the hashes don't match, it should be
- considered a failure and we should not continue searching the baseline list.
-
- Returns MATCH if the md5 provided matches the ones in the baseline json,
- MISMATCH if it does not, NO_BASELINE if the test case has no baseline, or
- BASELINE_DOWNLOAD_FAILED if the baseline could not be downloaded and parsed.
- """
- if self._baselines is None:
- return GoldBaseline.BASELINE_DOWNLOAD_FAILED
-
- found_test_case = False
- if test_name in self._baselines:
- found_test_case = True
- if md5_hash in self._baselines[test_name]:
- return GoldBaseline.MATCH
-
- return (GoldBaseline.MISMATCH
- if found_test_case else GoldBaseline.NO_BASELINE)
-
-
-# This module collects and writes output in a format expected by the
-# Gold baseline tool. Based on meta data provided explicitly and by
-# adding a series of test results it can be used to produce
-# a JSON file that is uploaded to Google Storage and ingested by Gold.
-#
-# The output will look similar this:
-#
-# {
-# "build_number" : "2",
-# "gitHash" : "a4a338179013b029d6dd55e737b5bd648a9fb68c",
-# "key" : {
-# "arch" : "arm64",
-# "compiler" : "Clang",
-# },
-# "results" : [
-# {
-# "key" : {
-# "config" : "vk",
-# "name" : "yuv_nv12_to_rgb_effect",
-# "source_type" : "gm"
-# },
-# "md5" : "7db34da246868d50ab9ddd776ce6d779",
-# "options" : {
-# "ext" : "png",
-# "gamma_correct" : "no"
-# }
-# },
-# {
-# "key" : {
-# "config" : "vk",
-# "name" : "yuv_to_rgb_effect",
-# "source_type" : "gm"
-# },
-# "md5" : "0b955f387740c66eb23bf0e253c80d64",
-# "options" : {
-# "ext" : "png",
-# "gamma_correct" : "no"
-# }
-# }
-# ],
-# }
-#
-class GoldResults(object):
-
- def __init__(self, source_type, output_dir, properties_str, key_str,
- ignore_hashes_file):
- """
- source_type is the source_type (=corpus) field used for all results.
- output_dir is the directory where the resulting images are copied and
- the dm.json file is written. If the directory exists it will
- be removed and recreated.
- properties_str is a string with space separated key/value pairs that
- is used to set the top level fields in the output JSON file.
- key_str is a string with space separated key/value pairs that
- is used to set the 'key' field in the output JSON file.
- ignore_hashes_file is a file that contains a list of image hashes
- that should be ignored.
- """
- self._source_type = source_type
- self._properties = _ParseKeyValuePairs(properties_str)
- self._properties['key'] = _ParseKeyValuePairs(key_str)
- self._results = []
- self._passfail = []
- self._output_dir = output_dir
-
- # make sure the output directory exists and is empty.
- if os.path.exists(output_dir):
- shutil.rmtree(output_dir, ignore_errors=True)
- os.makedirs(output_dir)
-
- self._ignore_hashes = set()
- if ignore_hashes_file:
- with open(ignore_hashes_file, 'r') as ig_file:
- hashes = [x.strip() for x in ig_file.readlines() if x.strip()]
- self._ignore_hashes = set(hashes)
-
- def AddTestResult(self, testName, md5Hash, outputImagePath, matchResult):
- # If the hash is in the list of hashes to ignore then we don'try
- # make a copy, but add it to the result.
- imgExt = os.path.splitext(outputImagePath)[1].lstrip('.')
- if md5Hash not in self._ignore_hashes:
- # Copy the image to <output_dir>/<md5Hash>.<image_extension>
- if not imgExt:
- raise ValueError('File %s does not have an extension' % outputImagePath)
- newFilePath = os.path.join(self._output_dir, md5Hash + '.' + imgExt)
- shutil.copy2(outputImagePath, newFilePath)
-
- # Add an entry to the list of test results
- self._results.append({
- 'key': {
- 'name': testName,
- 'source_type': self._source_type,
- },
- 'md5': md5Hash,
- 'options': {
- 'ext': imgExt,
- 'gamma_correct': 'no'
- }
- })
-
- self._passfail.append((testName, matchResult))
-
- def WriteResults(self):
- self._properties.update({'results': self._results})
-
- output_file_name = os.path.join(self._output_dir, 'dm.json')
- with open(output_file_name, 'wb') as outfile:
- json.dump(self._properties, outfile, indent=1)
- outfile.write('\n')
-
- output_file_name = os.path.join(self._output_dir, 'passfail.json')
- with open(output_file_name, 'wb') as outfile:
- json.dump(self._passfail, outfile, indent=1)
- outfile.write('\n')
-
-
-# Produce example output for manual testing.
-def _Example():
- # Create a test directory with three empty 'image' files.
- test_dir = './testdirectory'
- if not os.path.exists(test_dir):
- os.makedirs(test_dir)
- open(os.path.join(test_dir, 'image1.png'), 'wb').close()
- open(os.path.join(test_dir, 'image2.png'), 'wb').close()
- open(os.path.join(test_dir, 'image3.png'), 'wb').close()
-
- # Create an instance and add results.
- prop_str = 'build_number 2 "builder name" Builder-Name gitHash ' \
- 'a4a338179013b029d6dd55e737b5bd648a9fb68c'
-
- key_str = 'arch arm64 compiler Clang configuration Debug'
-
- hash_file = os.path.join(test_dir, 'ignore_hashes.txt')
- with open(hash_file, 'wb') as f:
- f.write('\n'.join(['hash-1', 'hash-4']) + '\n')
-
- output_dir = './output_directory'
- gr = GoldResults('pdfium', output_dir, prop_str, key_str, hash_file)
- gr.AddTestResult('test-1', 'hash-1', os.path.join(test_dir, 'image1.png'),
- GoldBaseline.MATCH)
- gr.AddTestResult('test-2', 'hash-2', os.path.join(test_dir, 'image2.png'),
- GoldBaseline.MATCH)
- gr.AddTestResult('test-3', 'hash-3', os.path.join(test_dir, 'image3.png'),
- GoldBaseline.MISMATCH)
- gr.WriteResults()
-
-
-if __name__ == '__main__':
- _Example()
diff --git a/testing/tools/libcxx_check.py b/testing/tools/libcxx_check.py
new file mode 100755
index 0000000..ad6abea
--- /dev/null
+++ b/testing/tools/libcxx_check.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python3
+# Copyright 2022 The PDFium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""Verifies libcxx_revision entries are in sync.
+
+DEPS and buildtools/deps_revisions.gni both have libcxx_revision entries.
+Check that they are in sync.
+"""
+
+import re
+import sys
+
+
+def _ExtractRevisionFromFile(path, regex):
+ """Gets the revision by reading path and searching the lines using regex."""
+ data = open(path, 'rb').read().splitlines()
+ revision = None
+ for line in data:
+ match = regex.match(line)
+ if not match:
+ continue
+ if revision:
+ return None
+ revision = match.group(1)
+ return revision
+
+
+def _GetDepsLibcxxRevision(deps_path):
+ """Gets the libcxx_revision from DEPS."""
+ regex = re.compile(b"^ 'libcxx_revision': '(.*)',$")
+ return _ExtractRevisionFromFile(deps_path, regex)
+
+
+def _GetBuildtoolsLibcxxRevision(buildtools_deps_path):
+ """Gets the libcxx_revision from buildtools/deps_revisions.gni."""
+ regex = re.compile(b'^ libcxx_revision = "(.*)"$')
+ return _ExtractRevisionFromFile(buildtools_deps_path, regex)
+
+
+def main():
+ if len(sys.argv) != 3:
+ print('Wrong number of arguments')
+ return 0
+
+ deps_path = sys.argv[1]
+ deps_revision = _GetDepsLibcxxRevision(deps_path)
+ if not deps_revision:
+ print('Cannot parse', deps_path)
+ return 0
+
+ buildtools_deps_path = sys.argv[2]
+ buildtools_revision = _GetBuildtoolsLibcxxRevision(buildtools_deps_path)
+ if not buildtools_revision:
+ print('Cannot parse', buildtools_deps_path)
+ return 0
+
+ if deps_revision != buildtools_revision:
+ print('libcxx_revision mismatch between %s and %s: %s vs. %s' %
+ (deps_path, buildtools_deps_path, deps_revision, buildtools_revision))
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/testing/tools/make_expected.sh b/testing/tools/make_expected.sh
index 5237e51..8f8238c 100755
--- a/testing/tools/make_expected.sh
+++ b/testing/tools/make_expected.sh
@@ -1,6 +1,6 @@
#!/bin/bash
#
-# Copyright 2015 PDFium Authors. All rights reserved.
+# Copyright 2015 The PDFium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
#
@@ -23,7 +23,9 @@
if [ -f "$EVTFILE" ]; then
SEND_EVENTS="--send-events"
fi
- out/Debug/pdfium_test $SEND_EVENTS --time=$TEST_SEED_TIME --png $INFILE
+ FONT_DIR=`readlink -f third_party/test_fonts`
+ out/Debug/pdfium_test $SEND_EVENTS --time=$TEST_SEED_TIME --png \
+ --croscore-font-names --font-dir=$FONT_DIR $INFILE
RESULTS="$INFILE.*.png"
for RESULT in $RESULTS ; do
EXPECTED=`echo -n $RESULT | sed 's/[.]pdf[.]/_expected.pdf./'`
diff --git a/testing/tools/pdfium_root.py b/testing/tools/pdfium_root.py
new file mode 100644
index 0000000..7c4c861
--- /dev/null
+++ b/testing/tools/pdfium_root.py
@@ -0,0 +1,53 @@
+# Copyright 2022 The PDFium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""Utilities for working with the PDFium tree's root directory."""
+
+import os
+import sys
+
+
+class RootDirectoryFinder:
+ """Finds the PDFium tree's root directories.
+
+ The implementation expects that either:
+ 1. PDFium is a standalone checkout.
+ 2. PDFium is part of another tree within a "third_party/pdfium" directory.
+
+ Attributes:
+ pdfium_root: The path to the root of the PDFium tree.
+ source_root: The path to the root of the source tree. May differ from
+ `pdfium_root` if PDFium is a third-party dependency in another tree.
+ """
+
+ def __init__(self):
+ # Expect `self_dir` to be ".../testing/tools".
+ self_dir = os.path.dirname(os.path.realpath(__file__))
+
+ self.pdfium_root = _remove_path_suffix(self_dir, ('testing', 'tools'))
+ if not self.pdfium_root:
+ raise Exception('Cannot find testing/tools within PDFium root directory')
+
+ # In a Chromium checkout, expect `self.pdfium_root` to be
+ # ".../third_party/pdfium".
+ self.source_root = _remove_path_suffix(self.pdfium_root,
+ ('third_party', 'pdfium'))
+ if not self.source_root:
+ self.source_root = self.pdfium_root
+
+
+def _remove_path_suffix(path, expected_suffix):
+ for expected_part in reversed(expected_suffix):
+ if os.path.basename(path) != expected_part:
+ return None
+ path = os.path.dirname(path)
+ return path
+
+
+def add_source_directory_to_import_path(source_directory_path):
+ """Adds a source root-relative directory to end of the import path."""
+ root_finder = RootDirectoryFinder()
+ path = os.path.realpath(
+ os.path.join(root_finder.source_root, source_directory_path))
+ if path not in sys.path:
+ sys.path.append(path)
diff --git a/testing/tools/pngdiffer.py b/testing/tools/pngdiffer.py
index a469506..2044a34 100755
--- a/testing/tools/pngdiffer.py
+++ b/testing/tools/pngdiffer.py
@@ -1,139 +1,239 @@
-#!/usr/bin/env python
-# Copyright 2015 The PDFium Authors. All rights reserved.
+#!/usr/bin/env python3
+# Copyright 2015 The PDFium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import distutils.spawn
+from dataclasses import dataclass
import itertools
import os
import shutil
+import subprocess
import sys
-# pylint: disable=relative-import
-import common
+EXACT_MATCHING = 'exact'
+FUZZY_MATCHING = 'fuzzy'
+_PNG_OPTIMIZER = 'optipng'
+
+_COMMON_SUFFIX_ORDER = ('_{os}', '')
+_AGG_SUFFIX_ORDER = ('_agg_{os}', '_agg') + _COMMON_SUFFIX_ORDER
+_SKIA_SUFFIX_ORDER = ('_skia_{os}', '_skia') + _COMMON_SUFFIX_ORDER
+
+
+@dataclass
+class ImageDiff:
+ """Details about an image diff.
+
+ Attributes:
+ actual_path: Path to the actual image file.
+ expected_path: Path to the expected image file, or `None` if no matches.
+ diff_path: Path to the diff image file, or `None` if no diff.
+ reason: Optional reason for the diff.
+ """
+ actual_path: str
+ expected_path: str = None
+ diff_path: str = None
+ reason: str = None
class PNGDiffer():
- def __init__(self, finder, reverse_byte_order):
+ def __init__(self, finder, features, reverse_byte_order):
self.pdfium_diff_path = finder.ExecutablePath('pdfium_diff')
self.os_name = finder.os_name
self.reverse_byte_order = reverse_byte_order
+ if 'SKIA' in features:
+ self.suffix_order = _SKIA_SUFFIX_ORDER
+ else:
+ self.suffix_order = _AGG_SUFFIX_ORDER
def CheckMissingTools(self, regenerate_expected):
- if (regenerate_expected and self.os_name == 'linux' and
- not distutils.spawn.find_executable('optipng')):
- return 'Please install "optipng" to regenerate expected images.'
+ if regenerate_expected and not shutil.which(_PNG_OPTIMIZER):
+ return f'Please install "{_PNG_OPTIMIZER}" to regenerate expected images.'
return None
def GetActualFiles(self, input_filename, source_dir, working_dir):
actual_paths = []
- path_templates = PathTemplates(input_filename, source_dir, working_dir)
+ path_templates = _PathTemplates(input_filename, source_dir, working_dir,
+ self.os_name, self.suffix_order)
for page in itertools.count():
actual_path = path_templates.GetActualPath(page)
- expected_path = path_templates.GetExpectedPath(page)
- platform_expected_path = path_templates.GetPlatformExpectedPath(
- self.os_name, page)
- if os.path.exists(platform_expected_path):
- expected_path = platform_expected_path
- elif not os.path.exists(expected_path):
+ if path_templates.GetExpectedPath(page, default_to_base=False):
+ actual_paths.append(actual_path)
+ else:
break
- actual_paths.append(actual_path)
-
return actual_paths
- def HasDifferences(self, input_filename, source_dir, working_dir):
- path_templates = PathTemplates(input_filename, source_dir, working_dir)
+ def _RunCommand(self, cmd):
+ try:
+ subprocess.run(cmd, capture_output=True, check=True)
+ return None
+ except subprocess.CalledProcessError as e:
+ return e
+ def _RunImageCompareCommand(self, image_diff, image_matching_algorithm):
+ cmd = [self.pdfium_diff_path]
+ if self.reverse_byte_order:
+ cmd.append('--reverse-byte-order')
+ if image_matching_algorithm == FUZZY_MATCHING:
+ cmd.append('--fuzzy')
+ cmd.extend([image_diff.actual_path, image_diff.expected_path])
+ return self._RunCommand(cmd)
+
+ def _RunImageDiffCommand(self, image_diff):
+ # TODO(crbug.com/pdfium/1925): Diff mode ignores --reverse-byte-order.
+ return self._RunCommand([
+ self.pdfium_diff_path, '--subtract', image_diff.actual_path,
+ image_diff.expected_path, image_diff.diff_path
+ ])
+
+ def ComputeDifferences(self, input_filename, source_dir, working_dir,
+ image_matching_algorithm):
+ """Computes differences between actual and expected image files.
+
+ Returns:
+ A list of `ImageDiff` instances, one per differing page.
+ """
+ image_diffs = []
+
+ path_templates = _PathTemplates(input_filename, source_dir, working_dir,
+ self.os_name, self.suffix_order)
for page in itertools.count():
- actual_path = path_templates.GetActualPath(page)
+ page_diff = ImageDiff(actual_path=path_templates.GetActualPath(page))
+ if not os.path.exists(page_diff.actual_path):
+ # No more actual pages.
+ break
+
expected_path = path_templates.GetExpectedPath(page)
- # PDFium tests should be platform independent. Platform based results are
- # used to capture platform dependent implementations.
- platform_expected_path = path_templates.GetPlatformExpectedPath(
- self.os_name, page)
- if (not os.path.exists(expected_path) and
- not os.path.exists(platform_expected_path)):
- if page == 0:
- print "WARNING: no expected results files for " + input_filename
- if os.path.exists(actual_path):
- print('FAILURE: Missing expected result for 0-based page %d of %s' %
- (page, input_filename))
- return True
- break
- print "Checking " + actual_path
- sys.stdout.flush()
if os.path.exists(expected_path):
- cmd = [self.pdfium_diff_path]
- if self.reverse_byte_order:
- cmd.append('--reverse-byte-order')
- cmd.extend([expected_path, actual_path])
- error = common.RunCommand(cmd)
+ page_diff.expected_path = expected_path
+
+ compare_error = self._RunImageCompareCommand(page_diff,
+ image_matching_algorithm)
+ if compare_error:
+ page_diff.reason = str(compare_error)
+
+ # TODO(crbug.com/pdfium/1925): Compare and diff simultaneously.
+ page_diff.diff_path = path_templates.GetDiffPath(page)
+ if not self._RunImageDiffCommand(page_diff):
+ print(f'WARNING: No diff for {page_diff.actual_path}')
+ page_diff.diff_path = None
+ else:
+ # Validate that no other paths match.
+ for unexpected_path in path_templates.GetExpectedPaths(page)[1:]:
+ page_diff.expected_path = unexpected_path
+ if not self._RunImageCompareCommand(page_diff,
+ image_matching_algorithm):
+ page_diff.reason = f'Also matches {unexpected_path}'
+ break
+ page_diff.expected_path = expected_path
else:
- error = 1
- if error:
- # When failed, we check against platform based results.
- if os.path.exists(platform_expected_path):
- cmd = [self.pdfium_diff_path]
- if self.reverse_byte_order:
- cmd.append('--reverse-byte-order')
- cmd.extend([platform_expected_path, actual_path])
- error = common.RunCommand(cmd)
- if error:
- print "FAILURE: " + input_filename + "; " + str(error)
- return True
+ if page == 0:
+ print(f'WARNING: no expected results files for {input_filename}')
+ page_diff.reason = f'{expected_path} does not exist'
- return False
+ if page_diff.reason:
+ image_diffs.append(page_diff)
- def Regenerate(self, input_filename, source_dir, working_dir, platform_only):
- path_templates = PathTemplates(input_filename, source_dir, working_dir)
+ return image_diffs
+ def Regenerate(self, input_filename, source_dir, working_dir,
+ image_matching_algorithm):
+ path_templates = _PathTemplates(input_filename, source_dir, working_dir,
+ self.os_name, self.suffix_order)
for page in itertools.count():
- # Loop through the generated page images. Stop when there is a page
- # missing a png, which means the document ended.
- actual_path = path_templates.GetActualPath(page)
- if not os.path.isfile(actual_path):
+ expected_paths = path_templates.GetExpectedPaths(page)
+
+ first_match = None
+ last_match = None
+ page_diff = ImageDiff(actual_path=path_templates.GetActualPath(page))
+ if os.path.exists(page_diff.actual_path):
+ # Match against all expected page images.
+ for index, expected_path in enumerate(expected_paths):
+ page_diff.expected_path = expected_path
+ if not self._RunImageCompareCommand(page_diff,
+ image_matching_algorithm):
+ if first_match is None:
+ first_match = index
+ last_match = index
+
+ if last_match == 0:
+ # Regeneration not needed. This case may be reached if only some, but
+ # not all, pages need to be regenerated.
+ continue
+ elif expected_paths:
+ # Remove all expected page images.
+ print(f'WARNING: {input_filename} has extra expected page {page}')
+ first_match = 0
+ last_match = len(expected_paths)
+ else:
+ # No more expected or actual pages.
break
- platform_expected_path = path_templates.GetPlatformExpectedPath(
- self.os_name, page)
+ # Try to reuse expectations by removing intervening non-matches.
+ #
+ # TODO(crbug.com/pdfium/1988): This can make mistakes due to a lack of
+ # global knowledge about other test configurations, which is why it just
+ # creates backup files rather than immediately removing files.
+ if last_match is not None:
+ if first_match > 1:
+ print(f'WARNING: {input_filename}.{page} has non-adjacent match')
+ if first_match != last_match:
+ print(f'WARNING: {input_filename}.{page} has redundant matches')
- # If there is a platform expected png, we will overwrite it. Otherwise,
- # overwrite the generic png in "all" mode, or do nothing in "platform"
- # mode.
- if os.path.exists(platform_expected_path):
- expected_path = platform_expected_path
- elif not platform_only:
- expected_path = path_templates.GetExpectedPath(page)
- else:
+ for expected_path in expected_paths[:last_match]:
+ os.rename(expected_path, expected_path + '.bak')
continue
- shutil.copyfile(actual_path, expected_path)
- common.RunCommand(['optipng', expected_path])
+ # Regenerate the most specific expected path that exists. If there are no
+ # existing expectations, regenerate the base case.
+ expected_path = path_templates.GetExpectedPath(page)
+ shutil.copyfile(page_diff.actual_path, expected_path)
+ self._RunCommand([_PNG_OPTIMIZER, expected_path])
-ACTUAL_TEMPLATE = '.pdf.%d.png'
-EXPECTED_TEMPLATE = '_expected' + ACTUAL_TEMPLATE
-PLATFORM_EXPECTED_TEMPLATE = '_expected_%s' + ACTUAL_TEMPLATE
+_ACTUAL_TEMPLATE = '.pdf.%d.png'
+_DIFF_TEMPLATE = '.pdf.%d.diff.png'
-class PathTemplates(object):
+class _PathTemplates:
- def __init__(self, input_filename, source_dir, working_dir):
+ def __init__(self, input_filename, source_dir, working_dir, os_name,
+ suffix_order):
input_root, _ = os.path.splitext(input_filename)
self.actual_path_template = os.path.join(working_dir,
- input_root + ACTUAL_TEMPLATE)
- self.expected_path = os.path.join(source_dir,
- input_root + EXPECTED_TEMPLATE)
- self.platform_expected_path = os.path.join(
- source_dir, input_root + PLATFORM_EXPECTED_TEMPLATE)
+ input_root + _ACTUAL_TEMPLATE)
+ self.diff_path_template = os.path.join(working_dir,
+ input_root + _DIFF_TEMPLATE)
+
+ # Pre-create the available templates from most to least specific. We
+ # generally expect the most specific case to match first.
+ self.expected_templates = []
+ for suffix in suffix_order:
+ formatted_suffix = suffix.format(os=os_name)
+ self.expected_templates.append(
+ os.path.join(
+ source_dir,
+ f'{input_root}_expected{formatted_suffix}{_ACTUAL_TEMPLATE}'))
+ assert self.expected_templates
def GetActualPath(self, page):
return self.actual_path_template % page
- def GetExpectedPath(self, page):
- return self.expected_path % page
+ def GetDiffPath(self, page):
+ return self.diff_path_template % page
- def GetPlatformExpectedPath(self, platform, page):
- return self.platform_expected_path % (platform, page)
+ def _GetPossibleExpectedPaths(self, page):
+ return [template % page for template in self.expected_templates]
+
+ def GetExpectedPaths(self, page):
+ return list(filter(os.path.exists, self._GetPossibleExpectedPaths(page)))
+
+ def GetExpectedPath(self, page, default_to_base=True):
+ """Returns the most specific expected path that exists."""
+ last_not_found_expected_path = None
+ for expected_path in self._GetPossibleExpectedPaths(page):
+ if os.path.exists(expected_path):
+ return expected_path
+ last_not_found_expected_path = expected_path
+ return last_not_found_expected_path if default_to_base else None
diff --git a/testing/tools/run_corpus_tests.py b/testing/tools/run_corpus_tests.py
index c1bec3a..c9ad185 100755
--- a/testing/tools/run_corpus_tests.py
+++ b/testing/tools/run_corpus_tests.py
@@ -1,18 +1,16 @@
-#!/usr/bin/env python
-# Copyright 2015 The PDFium Authors. All rights reserved.
+#!/usr/bin/env vpython3
+# Copyright 2015 The PDFium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import sys
-# pylint: disable=relative-import
import test_runner
def main():
runner = test_runner.TestRunner('corpus')
runner.SetEnforceExpectedImages(True)
- runner.SetOneShotRenderer(True)
return runner.Run()
diff --git a/testing/tools/run_javascript_tests.py b/testing/tools/run_javascript_tests.py
index 948c1af..013fe0b 100755
--- a/testing/tools/run_javascript_tests.py
+++ b/testing/tools/run_javascript_tests.py
@@ -1,11 +1,10 @@
-#!/usr/bin/env python
-# Copyright 2015 The PDFium Authors. All rights reserved.
+#!/usr/bin/env vpython3
+# Copyright 2015 The PDFium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import sys
-# pylint: disable=relative-import
import test_runner
diff --git a/testing/tools/run_pixel_tests.py b/testing/tools/run_pixel_tests.py
index 0992e0a..fb355b8 100755
--- a/testing/tools/run_pixel_tests.py
+++ b/testing/tools/run_pixel_tests.py
@@ -1,11 +1,10 @@
-#!/usr/bin/env python
-# Copyright 2015 The PDFium Authors. All rights reserved.
+#!/usr/bin/env vpython3
+# Copyright 2015 The PDFium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import sys
-# pylint: disable=relative-import
import test_runner
diff --git a/testing/tools/safetynet_compare.py b/testing/tools/safetynet_compare.py
index c76ce44..e1b5c85 100755
--- a/testing/tools/safetynet_compare.py
+++ b/testing/tools/safetynet_compare.py
@@ -1,5 +1,5 @@
-#!/usr/bin/env python
-# Copyright 2017 The PDFium Authors. All rights reserved.
+#!/usr/bin/env python3
+# Copyright 2017 The PDFium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Compares the performance of two versions of the pdfium code."""
@@ -16,7 +16,6 @@
import sys
import tempfile
-# pylint: disable=relative-import
from common import GetBooleanGnArg
from common import PrintErr
from common import RunCommandPropagateErr
@@ -33,7 +32,7 @@
return (test_case, result)
-class CompareRun(object):
+class CompareRun:
"""A comparison between two branches of pdfium."""
def __init__(self, args):
@@ -527,8 +526,7 @@
output_filename = (
'callgrind.out.%s.%s' % (test_case.replace('/', '_'), run_label))
return os.path.join(self.args.output_dir, output_filename)
- else:
- return None
+ return None
def _DrawConclusions(self, times_before_branch, times_after_branch):
"""Draws conclusions comparing results of test runs in two branches.
@@ -564,7 +562,7 @@
ComparisonConclusions.GetOutputDict().
"""
if self.args.machine_readable:
- print json.dumps(conclusions_dict)
+ print(json.dumps(conclusions_dict))
else:
PrintConclusionsDictHumanReadable(
conclusions_dict, colored=True, key=self.args.case_order)
diff --git a/testing/tools/safetynet_conclusions.py b/testing/tools/safetynet_conclusions.py
index 8f0b28c..b5be14a 100644
--- a/testing/tools/safetynet_conclusions.py
+++ b/testing/tools/safetynet_conclusions.py
@@ -1,4 +1,4 @@
-# Copyright 2017 The PDFium Authors. All rights reserved.
+# Copyright 2017 The PDFium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Classes that draw conclusions out of a comparison and represent them."""
@@ -31,7 +31,7 @@
}
-class ComparisonConclusions(object):
+class ComparisonConclusions:
"""All conclusions drawn from a comparison.
This is initialized empty and then processes pairs of results for each test
@@ -182,7 +182,7 @@
return output_dict
-class ComparisonSummary(object):
+class ComparisonSummary:
"""Totals computed for a comparison."""
def __init__(self):
@@ -207,7 +207,7 @@
return result
-class CaseResult(object):
+class CaseResult:
"""The conclusion for the comparison of a single test case."""
def __init__(self, case_name, before, after, ratio, rating):
@@ -245,9 +245,9 @@
key: String with the CaseResult dictionary key to sort the cases.
"""
# Print header
- print '=' * 80
- print '{0:>11s} {1:>15s} {2}'.format('% Change', 'Time after', 'Test case')
- print '-' * 80
+ print('=' * 80)
+ print('{0:>11s} {1:>15s} {2}'.format('% Change', 'Time after', 'Test case'))
+ print('-' * 80)
color = FORMAT_NORMAL
@@ -264,18 +264,18 @@
color = RATING_TO_COLOR[case_dict['rating']]
if case_dict['rating'] == RATING_FAILURE:
- print u'{} to measure time for {}'.format(
- color.format('Failed'), case_name).encode('utf-8')
+ print(u'{} to measure time for {}'.format(
+ color.format('Failed'), case_name).encode('utf-8'))
continue
- print u'{0} {1:15,d} {2}'.format(
+ print(u'{0} {1:15,d} {2}'.format(
color.format('{:+11.4%}'.format(case_dict['ratio'])),
- case_dict['after'], case_name).encode('utf-8')
+ case_dict['after'], case_name).encode('utf-8'))
# Print totals
totals = conclusions_dict['summary']
- print '=' * 80
- print 'Test cases run: %d' % totals['total']
+ print('=' * 80)
+ print('Test cases run: %d' % totals['total'])
if colored:
color = FORMAT_MAGENTA if totals[RATING_FAILURE] else FORMAT_GREEN
diff --git a/testing/tools/safetynet_image.py b/testing/tools/safetynet_image.py
index f300615..3295387 100644
--- a/testing/tools/safetynet_image.py
+++ b/testing/tools/safetynet_image.py
@@ -1,4 +1,4 @@
-# Copyright 2017 The PDFium Authors. All rights reserved.
+# Copyright 2017 The PDFium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Compares pairs of page images and generates an HTML to look at differences.
@@ -13,7 +13,6 @@
import sys
import webbrowser
-# pylint: disable=relative-import
from common import DirectoryFinder
@@ -21,7 +20,7 @@
return image_comparison.GenerateOneDiff(image)
-class ImageComparison(object):
+class ImageComparison:
"""Compares pairs of page images and generates an HTML to look at differences.
The images are all assumed to have the same name and be in two directories:
@@ -63,7 +62,7 @@
# pylint: disable=attribute-defined-outside-init
if len(self.two_labels) != 2:
- print >> sys.stderr, 'two_labels must be a tuple of length 2'
+ print('two_labels must be a tuple of length 2', file=sys.stderr)
return 1
finder = DirectoryFinder(self.build_dir)
@@ -88,7 +87,7 @@
for image in self.image_locations.Images():
diff = difference[image]
if diff is None:
- print >> sys.stderr, 'Failed to compare image %s' % image
+ print('Failed to compare image %s' % image, file=sys.stderr)
elif diff > self.threshold:
self._WriteImageRows(f, image, diff)
else:
@@ -170,7 +169,7 @@
except subprocess.CalledProcessError as e:
return image, percentage_change
else:
- print >> sys.stderr, 'Warning: Should have failed the previous diff.'
+ print('Warning: Should have failed the previous diff.', file=sys.stderr)
return image, 0
def _GetRelativePath(self, absolute_path):
@@ -239,7 +238,7 @@
f.write('</td></tr>')
-class ImageLocations(object):
+class ImageLocations:
"""Contains the locations of input and output image files.
"""
diff --git a/testing/tools/safetynet_job.py b/testing/tools/safetynet_job.py
index 9b5cbfd..bbb3cca 100755
--- a/testing/tools/safetynet_job.py
+++ b/testing/tools/safetynet_job.py
@@ -1,5 +1,5 @@
-#!/usr/bin/env python
-# Copyright 2017 The PDFium Authors. All rights reserved.
+#!/usr/bin/env python3
+# Copyright 2017 The PDFium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Looks for performance regressions on all pushes since the last run.
@@ -15,14 +15,13 @@
import os
import sys
-# pylint: disable=relative-import
from common import PrintWithTime
from common import RunCommandPropagateErr
from githelper import GitHelper
from safetynet_conclusions import PrintConclusionsDictHumanReadable
-class JobContext(object):
+class JobContext:
"""Context for a single run, including name and directory paths."""
def __init__(self, args):
@@ -36,7 +35,7 @@
'%s.log' % self.datetime)
-class JobRun(object):
+class JobRun:
"""A single run looking for regressions since the last one."""
def __init__(self, args, context):
@@ -69,7 +68,7 @@
if not self.args.no_checkout:
self.git.FetchOriginMaster()
- self.git.Checkout('origin/master')
+ self.git.Checkout('origin/main')
# Make sure results dir exists
if not os.path.exists(self.context.results_dir):
@@ -200,7 +199,7 @@
parser.add_argument(
'--no-checkout',
action='store_true',
- help='whether to skip checking out origin/master. Use '
+ help='whether to skip checking out origin/main. Use '
'for script debugging.')
parser.add_argument(
'--no-checkpoint',
diff --git a/testing/tools/safetynet_measure.py b/testing/tools/safetynet_measure.py
index 3577189..cc83b1d 100755
--- a/testing/tools/safetynet_measure.py
+++ b/testing/tools/safetynet_measure.py
@@ -1,5 +1,5 @@
-#!/usr/bin/env python
-# Copyright 2017 The PDFium Authors. All rights reserved.
+#!/usr/bin/env python3
+# Copyright 2017 The PDFium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Measures performance for rendering a single test case with pdfium.
@@ -13,7 +13,6 @@
import subprocess
import sys
-# pylint: disable=relative-import
from common import PrintErr
CALLGRIND_PROFILER = 'callgrind'
@@ -23,7 +22,7 @@
PDFIUM_TEST = 'pdfium_test'
-class PerformanceRun(object):
+class PerformanceRun:
"""A single measurement of a test case."""
def __init__(self, args):
@@ -65,7 +64,7 @@
if time is None:
return 1
- print time
+ print(time)
return 0
def _RunCallgrind(self):
@@ -84,7 +83,8 @@
'--instr-atstart=%s' % instrument_at_start,
'--callgrind-out-file=%s' % output_path
] + self._BuildTestHarnessCommand())
- output = subprocess.check_output(valgrind_cmd, stderr=subprocess.STDOUT)
+ output = subprocess.check_output(
+ valgrind_cmd, stderr=subprocess.STDOUT).decode('utf-8')
# Match the line with the instruction count, eg.
# '==98765== Collected : 12345'
@@ -100,7 +100,8 @@
# -einstructions: print only instruction count
cmd_to_run = (['perf', 'stat', '--no-big-num', '-einstructions'] +
self._BuildTestHarnessCommand())
- output = subprocess.check_output(cmd_to_run, stderr=subprocess.STDOUT)
+ output = subprocess.check_output(
+ cmd_to_run, stderr=subprocess.STDOUT).decode('utf-8')
# Match the line with the instruction count, eg.
# ' 12345 instructions'
diff --git a/testing/tools/skia_gold/__init__.py b/testing/tools/skia_gold/__init__.py
new file mode 100644
index 0000000..39381b9
--- /dev/null
+++ b/testing/tools/skia_gold/__init__.py
@@ -0,0 +1,7 @@
+# Copyright 2022 The PDFium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import pdfium_root
+
+pdfium_root.add_source_directory_to_import_path('build')
diff --git a/testing/tools/skia_gold/pdfium_skia_gold_properties.py b/testing/tools/skia_gold/pdfium_skia_gold_properties.py
new file mode 100644
index 0000000..aeb8ef9
--- /dev/null
+++ b/testing/tools/skia_gold/pdfium_skia_gold_properties.py
@@ -0,0 +1,27 @@
+# Copyright 2021 The PDFium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""PDFium implementation of //build/skia_gold_common/skia_gold_properties.py."""
+
+import subprocess
+import sys
+
+import pdfium_root
+from skia_gold_common import skia_gold_properties
+
+
+class PDFiumSkiaGoldProperties(skia_gold_properties.SkiaGoldProperties):
+
+ @staticmethod
+ def _GetGitOriginMainHeadSha1():
+ root_finder = pdfium_root.RootDirectoryFinder()
+ try:
+ return subprocess.check_output(['git', 'rev-parse', 'origin/main'],
+ shell=_IsWin(),
+ cwd=root_finder.pdfium_root).strip()
+ except subprocess.CalledProcessError:
+ return None
+
+
+def _IsWin():
+ return sys.platform == 'win32'
diff --git a/testing/tools/skia_gold/pdfium_skia_gold_session.py b/testing/tools/skia_gold/pdfium_skia_gold_session.py
new file mode 100644
index 0000000..98a7ef2
--- /dev/null
+++ b/testing/tools/skia_gold/pdfium_skia_gold_session.py
@@ -0,0 +1,29 @@
+# Copyright 2021 The PDFium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""PDFium implementation of //build/skia_gold_common/skia_gold_session.py."""
+
+from skia_gold_common import output_managerless_skia_gold_session as omsgs
+
+
+# ComparisonResults nested inside the SkiaGoldSession causes issues with
+# multiprocessing and pickling, so it was moved out here.
+class PDFiumComparisonResults:
+ """Struct-like object for storing results of an image comparison."""
+
+ def __init__(self):
+ self.public_triage_link = None
+ self.internal_triage_link = None
+ self.triage_link_omission_reason = None
+ self.local_diff_given_image = None
+ self.local_diff_closest_image = None
+ self.local_diff_diff_image = None
+
+
+class PDFiumSkiaGoldSession(omsgs.OutputManagerlessSkiaGoldSession):
+
+ def _GetDiffGoldInstance(self):
+ return str(self._instance)
+
+ def ComparisonResults(self):
+ return PDFiumComparisonResults()
diff --git a/testing/tools/skia_gold/pdfium_skia_gold_session_manager.py b/testing/tools/skia_gold/pdfium_skia_gold_session_manager.py
new file mode 100644
index 0000000..af6cc5e
--- /dev/null
+++ b/testing/tools/skia_gold/pdfium_skia_gold_session_manager.py
@@ -0,0 +1,21 @@
+# Copyright 2021 The PDFium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""PDFium implementation of
+//build/skia_gold_common/skia_gold_session_manager.py."""
+
+from . import pdfium_skia_gold_session
+from skia_gold_common import skia_gold_session_manager as sgsm
+
+SKIA_PDF_INSTANCE = 'pdfium'
+
+
+class PDFiumSkiaGoldSessionManager(sgsm.SkiaGoldSessionManager):
+
+ @staticmethod
+ def GetSessionClass():
+ return pdfium_skia_gold_session.PDFiumSkiaGoldSession
+
+ @staticmethod
+ def _GetDefaultInstance():
+ return SKIA_PDF_INSTANCE
diff --git a/testing/tools/skia_gold/skia_gold.py b/testing/tools/skia_gold/skia_gold.py
new file mode 100644
index 0000000..e3595d2
--- /dev/null
+++ b/testing/tools/skia_gold/skia_gold.py
@@ -0,0 +1,221 @@
+# Copyright 2021 The PDFium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import logging
+import shlex
+import shutil
+
+from . import pdfium_skia_gold_properties
+from . import pdfium_skia_gold_session_manager
+
+GS_BUCKET = 'skia-pdfium-gm'
+
+
+def _ParseKeyValuePairs(kv_str):
+ """
+ Parses a string of the type 'key1 value1 key2 value2' into a dict.
+ """
+ kv_pairs = shlex.split(kv_str)
+ if len(kv_pairs) % 2:
+ raise ValueError('Uneven number of key/value pairs. Got %s' % kv_str)
+ return {kv_pairs[i]: kv_pairs[i + 1] for i in range(0, len(kv_pairs), 2)}
+
+
+def add_skia_gold_args(parser):
+ group = parser.add_argument_group('Skia Gold Arguments')
+ group.add_argument(
+ '--git-revision', help='Revision being tested.', default=None)
+ group.add_argument(
+ '--gerrit-issue',
+ help='For Skia Gold integration. Gerrit issue ID.',
+ default='')
+ group.add_argument(
+ '--gerrit-patchset',
+ help='For Skia Gold integration. Gerrit patch set number.',
+ default='')
+ group.add_argument(
+ '--buildbucket-id',
+ help='For Skia Gold integration. Buildbucket build ID.',
+ default='')
+ group.add_argument(
+ '--bypass-skia-gold-functionality',
+ action='store_true',
+ default=False,
+ help='Bypass all interaction with Skia Gold, effectively disabling the '
+ 'image comparison portion of any tests that use Gold. Only meant to '
+ 'be used in case a Gold outage occurs and cannot be fixed quickly.')
+ local_group = group.add_mutually_exclusive_group()
+ local_group.add_argument(
+ '--local-pixel-tests',
+ action='store_true',
+ default=None,
+ help='Specifies to run the test harness in local run mode or not. When '
+ 'run in local mode, uploading to Gold is disabled and links to '
+ 'help with local debugging are output. Running in local mode also '
+ 'implies --no-luci-auth. If both this and --no-local-pixel-tests are '
+ 'left unset, the test harness will attempt to detect whether it is '
+ 'running on a workstation or not and set this option accordingly.')
+ local_group.add_argument(
+ '--no-local-pixel-tests',
+ action='store_false',
+ dest='local_pixel_tests',
+ help='Specifies to run the test harness in non-local (bot) mode. When '
+ 'run in this mode, data is actually uploaded to Gold and triage links '
+ 'arge generated. If both this and --local-pixel-tests are left unset, '
+ 'the test harness will attempt to detect whether it is running on a '
+ 'workstation or not and set this option accordingly.')
+ group.add_argument(
+ '--no-luci-auth',
+ action='store_true',
+ default=False,
+ help='Don\'t use the service account provided by LUCI for '
+ 'authentication for Skia Gold, instead relying on gsutil to be '
+ 'pre-authenticated. Meant for testing locally instead of on the bots.')
+
+ group.add_argument(
+ '--gold_key',
+ default='',
+ dest="gold_key",
+ help='Key value pairs of config data such like the hardware/software '
+ 'configuration the image was produced on.')
+ group.add_argument(
+ '--gold_output_dir',
+ default='',
+ dest="gold_output_dir",
+ help='Path to the dir where diff output image files are saved, '
+ 'if running locally. If this is a tryjob run, will contain link to skia '
+ 'gold CL triage link. Required with --run-skia-gold.')
+
+
+def clear_gold_output_dir(output_dir):
+ # make sure the output directory exists and is empty.
+ if os.path.exists(output_dir):
+ shutil.rmtree(output_dir, ignore_errors=True)
+ os.makedirs(output_dir)
+
+
+class SkiaGoldTester:
+
+ def __init__(self, source_type, skia_gold_args, process_name=None):
+ """
+ source_type: source_type (=corpus) field used for all results.
+ skia_gold_args: Parsed arguments from argparse.ArgumentParser.
+ process_name: Unique name of current process, if multiprocessing is on.
+ """
+ self._source_type = source_type
+ self._output_dir = skia_gold_args.gold_output_dir
+ # goldctl is not thread safe, so each process needs its own directory
+ if process_name:
+ self._output_dir = os.path.join(skia_gold_args.gold_output_dir,
+ process_name)
+ clear_gold_output_dir(self._output_dir)
+ self._keys = _ParseKeyValuePairs(skia_gold_args.gold_key)
+ self._old_gold_props = _ParseKeyValuePairs(skia_gold_args.gold_properties)
+ self._skia_gold_args = skia_gold_args
+ self._skia_gold_session_manager = None
+ self._skia_gold_properties = None
+
+ def WriteCLTriageLink(self, link):
+ # pdfium recipe will read from this file and display the link in the step
+ # presentation
+ assert isinstance(link, str)
+ output_file_name = os.path.join(self._output_dir, 'cl_triage_link.txt')
+ if os.path.exists(output_file_name):
+ os.remove(output_file_name)
+ with open(output_file_name, 'wb') as outfile:
+ outfile.write(link.encode('utf8'))
+
+ def GetSkiaGoldProperties(self):
+ if not self._skia_gold_properties:
+ if self._old_gold_props:
+ self._skia_gold_args.git_revision = self._old_gold_props['gitHash']
+ self._skia_gold_args.gerrit_issue = self._old_gold_props['issue']
+ self._skia_gold_args.gerrit_patchset = self._old_gold_props['patchset']
+ self._skia_gold_args.buildbucket_id = \
+ self._old_gold_props['buildbucket_build_id']
+
+ if self._skia_gold_args.local_pixel_tests is None:
+ self._skia_gold_args.local_pixel_tests = 'SWARMING_SERVER' \
+ not in os.environ
+
+ self._skia_gold_properties = pdfium_skia_gold_properties\
+ .PDFiumSkiaGoldProperties(self._skia_gold_args)
+ return self._skia_gold_properties
+
+ def GetSkiaGoldSessionManager(self):
+ if not self._skia_gold_session_manager:
+ self._skia_gold_session_manager = pdfium_skia_gold_session_manager\
+ .PDFiumSkiaGoldSessionManager(self._output_dir,
+ self.GetSkiaGoldProperties())
+ return self._skia_gold_session_manager
+
+ def IsTryjobRun(self):
+ return self.GetSkiaGoldProperties().IsTryjobRun()
+
+ def GetCLTriageLink(self):
+ return 'https://pdfium-gold.skia.org/search?issue={issue}&crs=gerrit&'\
+ 'corpus={source_type}'.format(
+ issue=self.GetSkiaGoldProperties().issue, source_type=self._source_type)
+
+ def UploadTestResultToSkiaGold(self, image_name, image_path):
+ gold_properties = self.GetSkiaGoldProperties()
+ use_luci = not (gold_properties.local_pixel_tests or
+ gold_properties.no_luci_auth)
+ gold_session = self.GetSkiaGoldSessionManager()\
+ .GetSkiaGoldSession(self._keys, corpus=self._source_type,
+ bucket=GS_BUCKET)
+
+ status, error = gold_session.RunComparison(
+ name=image_name, png_file=image_path, use_luci=use_luci)
+
+ status_codes =\
+ self.GetSkiaGoldSessionManager().GetSessionClass().StatusCodes
+ if status == status_codes.SUCCESS:
+ return True
+ if status == status_codes.AUTH_FAILURE:
+ logging.error('Gold authentication failed with output %s', error)
+ elif status == status_codes.INIT_FAILURE:
+ logging.error('Gold initialization failed with output %s', error)
+ elif status == status_codes.COMPARISON_FAILURE_REMOTE:
+ logging.error('Remote comparison failed. See outputted triage links.')
+ elif status == status_codes.COMPARISON_FAILURE_LOCAL:
+ logging.error('Local comparison failed. Local diff files:')
+ _OutputLocalDiffFiles(gold_session, image_name)
+ print()
+ elif status == status_codes.LOCAL_DIFF_FAILURE:
+ logging.error(
+ 'Local comparison failed and an error occurred during diff '
+ 'generation: %s', error)
+ # There might be some files, so try outputting them.
+ logging.error('Local diff files:')
+ _OutputLocalDiffFiles(gold_session, image_name)
+ print()
+ else:
+ logging.error(
+ 'Given unhandled SkiaGoldSession StatusCode %s with error %s', status,
+ error)
+
+ return False
+
+
+def _OutputLocalDiffFiles(gold_session, image_name):
+ """Logs the local diff image files from the given SkiaGoldSession.
+
+ Args:
+ gold_session: A skia_gold_session.SkiaGoldSession instance to pull files
+ from.
+ image_name: A string containing the name of the image/test that was
+ compared.
+ """
+ given_file = gold_session.GetGivenImageLink(image_name)
+ closest_file = gold_session.GetClosestImageLink(image_name)
+ diff_file = gold_session.GetDiffImageLink(image_name)
+ failure_message = 'Unable to retrieve link'
+ logging.error('Generated image for %s: %s', image_name, given_file or
+ failure_message)
+ logging.error('Closest image for %s: %s', image_name, closest_file or
+ failure_message)
+ logging.error('Diff image for %s: %s', image_name, diff_file or
+ failure_message)
diff --git a/testing/tools/strip_jp2_comments.py b/testing/tools/strip_jp2_comments.py
new file mode 100755
index 0000000..eb03cdc
--- /dev/null
+++ b/testing/tools/strip_jp2_comments.py
@@ -0,0 +1,124 @@
+#!/usr/bin/env python3
+# Copyright 2023 The PDFium Authors
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""Strips comments from a JP2 file.
+
+This is a simple filter script to strip comments from a JP2 file, in order to
+save a few bytes from the final file size.
+"""
+
+import struct
+import sys
+
+BOX_HEADER_SIZE = 8
+BOX_TAG_JP2C = b'jp2c'
+
+MARKER_SIZE = 2
+MARKER_START = 0xff
+MARKER_TAG_IGNORE = 0x00
+MARKER_TAG_COMMENT = 0x64
+MARKER_TAG_FILL = 0xff
+
+
+def parse_box(buffer, offset):
+ """Parses the next box in a JP2 file.
+
+ Args:
+ buffer: A buffer containing the JP2 file contents.
+ offset: The starting offset into the buffer.
+
+ Returns:
+ A tuple (next_offset, tag) where next_offset is the ending offset, and tag
+ is the type tag. The box contents will be buffer[offset + 8:next_offset].
+ """
+ length, tag = struct.unpack_from('>I4s', buffer, offset)
+ return offset + length, tag
+
+
+def parse_marker(buffer, offset):
+ """Parses the next marker in a codestream.
+
+ Args:
+ buffer: A buffer containing the codestream.
+ offset: The starting offset into the buffer.
+
+ Returns:
+ A tuple (next_offset, tag) where next_offset is the offset after the marker,
+ and tag is the type tag. If no marker was found, next_offset will point to
+ the end of the buffer, and tag will be None. A marker is always 2 bytes.
+ """
+ while True:
+ # Search for start of marker.
+ next_offset = buffer.find(MARKER_START, offset)
+ if next_offset == -1:
+ next_offset = len(buffer)
+ break
+ next_offset += 1
+
+ # Parse marker.
+ if next_offset == len(buffer):
+ break
+ tag = buffer[next_offset]
+ if tag == MARKER_TAG_FILL:
+ # Possible fill byte, reparse as start of marker.
+ continue
+ next_offset += 1
+
+ if tag == MARKER_TAG_IGNORE:
+ # Not a real marker.
+ continue
+ return next_offset, tag
+
+ return next_offset
+
+
+def rewrite_jp2c(buffer):
+ rewrite_buffer = bytearray(BOX_HEADER_SIZE)
+
+ offset = 0
+ start_offset = offset
+ while offset < len(buffer):
+ next_offset, marker = parse_marker(buffer, offset)
+ if marker == MARKER_TAG_COMMENT:
+ # Flush the codestream before the comment.
+ rewrite_buffer.extend(buffer[start_offset:next_offset - MARKER_SIZE])
+
+ # Find the next marker, skipping the comment.
+ next_offset, marker = parse_marker(buffer, next_offset)
+ if marker is not None:
+ # Reparse the marker.
+ next_offset -= MARKER_SIZE
+ start_offset = next_offset
+ else:
+ # Pass through other markers.
+ pass
+ offset = next_offset
+
+ # Flush the tail of the codestream.
+ rewrite_buffer.extend(buffer[start_offset:])
+
+ struct.pack_into('>I4s', rewrite_buffer, 0, len(rewrite_buffer), BOX_TAG_JP2C)
+ return rewrite_buffer
+
+
+def main(in_file, out_file):
+ buffer = in_file.read()
+
+ # Scan through JP2 boxes.
+ offset = 0
+ while offset < len(buffer):
+ next_offset, tag = parse_box(buffer, offset)
+ if tag == BOX_TAG_JP2C:
+ # Rewrite "jp2c" (codestream) box.
+ out_file.write(rewrite_jp2c(buffer[offset + BOX_HEADER_SIZE:next_offset]))
+ else:
+ # Pass through other boxes.
+ out_file.write(buffer[offset:next_offset])
+ offset = next_offset
+
+ out_file.flush()
+
+
+if __name__ == '__main__':
+ main(sys.stdin.buffer, sys.stdout.buffer)
diff --git a/testing/tools/suppressor.py b/testing/tools/suppressor.py
index 70eef99..989f4dd 100755
--- a/testing/tools/suppressor.py
+++ b/testing/tools/suppressor.py
@@ -1,31 +1,34 @@
-#!/usr/bin/env python
-# Copyright 2015 The PDFium Authors. All rights reserved.
+#!/usr/bin/env python3
+# Copyright 2015 The PDFium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import os
-# pylint: disable=relative-import
import common
+import pngdiffer
class Suppressor:
- def __init__(self, finder, feature_string, js_disabled, xfa_disabled):
- feature_vector = feature_string.strip().split(",")
- self.has_v8 = not js_disabled and "V8" in feature_vector
- self.has_xfa = (not js_disabled and not xfa_disabled and
- "XFA" in feature_vector)
+ def __init__(self, finder, features, js_disabled, xfa_disabled):
+ self.has_v8 = not js_disabled and 'V8' in features
+ self.has_xfa = not js_disabled and not xfa_disabled and 'XFA' in features
+ self.has_skia = 'SKIA' in features
self.suppression_set = self._LoadSuppressedSet('SUPPRESSIONS', finder)
self.image_suppression_set = self._LoadSuppressedSet(
'SUPPRESSIONS_IMAGE_DIFF', finder)
+ self.exact_matching_suppression_set = self._LoadSuppressedSet(
+ 'SUPPRESSIONS_EXACT_MATCHING', finder)
def _LoadSuppressedSet(self, suppressions_filename, finder):
v8_option = "v8" if self.has_v8 else "nov8"
xfa_option = "xfa" if self.has_xfa else "noxfa"
+ rendering_option = "skia" if self.has_skia else "agg"
with open(os.path.join(finder.TestingDir(), suppressions_filename)) as f:
return set(
- self._FilterSuppressions(common.os_name(), v8_option, xfa_option,
+ self._FilterSuppressions(common.os_name(), v8_option,
+ xfa_option, rendering_option,
self._ExtractSuppressions(f)))
def _ExtractSuppressions(self, f):
@@ -34,35 +37,45 @@
for x in f.readlines()] if y
]
- def _FilterSuppressions(self, os_name, js, xfa, unfiltered_list):
+ def _FilterSuppressions(self, os_name, js, xfa, rendering_option,
+ unfiltered_list):
return [
x[0]
for x in unfiltered_list
- if self._MatchSuppression(x, os_name, js, xfa)
+ if self._MatchSuppression(x, os_name, js, xfa, rendering_option)
]
- def _MatchSuppression(self, item, os_name, js, xfa):
+ def _MatchSuppression(self, item, os_name, js, xfa, rendering_option):
os_column = item[1].split(",")
js_column = item[2].split(",")
xfa_column = item[3].split(",")
+ rendering_option_column = item[4].split(",")
return (('*' in os_column or os_name in os_column) and
('*' in js_column or js in js_column) and
- ('*' in xfa_column or xfa in xfa_column))
+ ('*' in xfa_column or xfa in xfa_column) and
+ ('*' in rendering_option_column or
+ rendering_option in rendering_option_column))
def IsResultSuppressed(self, input_filename):
if input_filename in self.suppression_set:
- print "%s result is suppressed" % input_filename
+ print("%s result is suppressed" % input_filename)
return True
return False
def IsExecutionSuppressed(self, input_filepath):
if "xfa_specific" in input_filepath and not self.has_xfa:
- print "%s execution is suppressed" % input_filepath
+ print("%s execution is suppressed" % input_filepath)
return True
return False
def IsImageDiffSuppressed(self, input_filename):
if input_filename in self.image_suppression_set:
- print "%s image diff comparison is suppressed" % input_filename
+ print("%s image diff comparison is suppressed" % input_filename)
return True
return False
+
+ def GetImageMatchingAlgorithm(self, input_filename):
+ if input_filename in self.exact_matching_suppression_set:
+ print(f"{input_filename} image diff comparison is fuzzy")
+ return pngdiffer.FUZZY_MATCHING
+ return pngdiffer.EXACT_MATCHING
diff --git a/testing/tools/test_runner.py b/testing/tools/test_runner.py
index d3640d6..ad348aa 100644
--- a/testing/tools/test_runner.py
+++ b/testing/tools/test_runner.py
@@ -1,23 +1,30 @@
-#!/usr/bin/env python
-# Copyright 2016 The PDFium Authors. All rights reserved.
+#!/usr/bin/env python3
+# Copyright 2016 The PDFium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-import functools
+import argparse
+from dataclasses import dataclass, field
+from datetime import timedelta
+from io import BytesIO
import multiprocessing
-import optparse
import os
import re
import shutil
import subprocess
import sys
+import time
-# pylint: disable=relative-import
import common
-import gold
+import pdfium_root
import pngdiffer
+from skia_gold import skia_gold
import suppressor
+pdfium_root.add_source_directory_to_import_path(os.path.join('build', 'util'))
+from lib.results import result_sink, result_types
+
+
# Arbitrary timestamp, expressed in seconds since the epoch, used to make sure
# that tests that depend on the current time are stable. Happens to be the
# timestamp of the first commit to repo, 2014/5/9 17:48:50.
@@ -26,33 +33,10 @@
# List of test types that should run text tests instead of pixel tests.
TEXT_TESTS = ['javascript']
-
-class KeyboardInterruptError(Exception):
- pass
-
-
-# Nomenclature:
-# x_root - "x"
-# x_filename - "x.ext"
-# x_path - "path/to/a/b/c/x.ext"
-# c_dir - "path/to/a/b/c"
-
-
-def TestOneFileParallel(this, test_case):
- """Wrapper to call GenerateAndTest() and redirect output to stdout."""
- try:
- input_filename, source_dir = test_case
- result = this.GenerateAndTest(input_filename, source_dir)
- return (result, input_filename, source_dir)
- except KeyboardInterrupt:
- raise KeyboardInterruptError()
-
-
-def DeleteFiles(files):
- """Utility function to delete a list of files"""
- for f in files:
- if os.path.exists(f):
- os.remove(f)
+# Timeout (in seconds) for individual test commands.
+# TODO(crbug.com/pdfium/1967): array_buffer.in is slow under MSan, so need a
+# very generous 5 minute timeout for now.
+TEST_TIMEOUT = timedelta(minutes=5).total_seconds()
class TestRunner:
@@ -62,391 +46,303 @@
# which all correspond directly to the type for the test being run. In the
# future if there are tests that don't have this clean correspondence, then
# an argument for the type will need to be added.
- self.test_dir = dirname
- self.test_type = dirname
- self.delete_output_on_success = False
- self.enforce_expected_images = False
- self.oneshot_renderer = False
+ self.per_process_config = _PerProcessConfig(
+ test_dir=dirname, test_type=dirname)
- # GenerateAndTest returns a tuple <success, outputfiles> where
- # success is a boolean indicating whether the tests passed comparison
- # tests and outputfiles is a list tuples:
- # (path_to_image, md5_hash_of_pixelbuffer)
- def GenerateAndTest(self, input_filename, source_dir):
- input_root, _ = os.path.splitext(input_filename)
- pdf_path = os.path.join(self.working_dir, input_root + '.pdf')
+ @property
+ def options(self):
+ return self.per_process_config.options
- # Remove any existing generated images from previous runs.
- actual_images = self.image_differ.GetActualFiles(input_filename, source_dir,
- self.working_dir)
- DeleteFiles(actual_images)
+ def IsSkiaGoldEnabled(self):
+ return (self.options.run_skia_gold and
+ not self.per_process_config.test_type in TEXT_TESTS)
- sys.stdout.flush()
+ def IsExecutionSuppressed(self, input_path):
+ return self.per_process_state.test_suppressor.IsExecutionSuppressed(
+ input_path)
- raised_exception = self.Generate(source_dir, input_filename, input_root,
- pdf_path)
+ def IsResultSuppressed(self, input_filename):
+ return self.per_process_state.test_suppressor.IsResultSuppressed(
+ input_filename)
- if raised_exception is not None:
- print 'FAILURE: %s; %s' % (input_filename, raised_exception)
- return False, []
+ def HandleResult(self, test_case, test_result):
+ input_filename = os.path.basename(test_case.input_path)
- results = []
- if self.test_type in TEXT_TESTS:
- expected_txt_path = os.path.join(source_dir, input_root + '_expected.txt')
- raised_exception = self.TestText(input_filename, input_root,
- expected_txt_path, pdf_path)
- else:
- use_ahem = 'use_ahem' in source_dir
- raised_exception, results = self.TestPixel(pdf_path, use_ahem)
-
- if raised_exception is not None:
- print 'FAILURE: %s; %s' % (input_filename, raised_exception)
- return False, results
-
- if actual_images:
- if self.image_differ.HasDifferences(input_filename, source_dir,
- self.working_dir):
- self.RegenerateIfNeeded_(input_filename, source_dir)
- return False, results
- else:
- if (self.enforce_expected_images and
- not self.test_suppressor.IsImageDiffSuppressed(input_filename)):
- self.RegenerateIfNeeded_(input_filename, source_dir)
- print 'FAILURE: %s; Missing expected images' % input_filename
- return False, results
-
- if self.delete_output_on_success:
- DeleteFiles(actual_images)
- return True, results
-
- def RegenerateIfNeeded_(self, input_filename, source_dir):
- if (not self.options.regenerate_expected or
- self.test_suppressor.IsResultSuppressed(input_filename) or
- self.test_suppressor.IsImageDiffSuppressed(input_filename)):
- return
-
- platform_only = (self.options.regenerate_expected == 'platform')
- self.image_differ.Regenerate(input_filename, source_dir, self.working_dir,
- platform_only)
-
- def Generate(self, source_dir, input_filename, input_root, pdf_path):
- original_path = os.path.join(source_dir, input_filename)
- input_path = os.path.join(source_dir, input_root + '.in')
-
- input_event_path = os.path.join(source_dir, input_root + '.evt')
- if os.path.exists(input_event_path):
- output_event_path = os.path.splitext(pdf_path)[0] + '.evt'
- shutil.copyfile(input_event_path, output_event_path)
-
- if not os.path.exists(input_path):
- if os.path.exists(original_path):
- shutil.copyfile(original_path, pdf_path)
- return None
-
- sys.stdout.flush()
-
- return common.RunCommand([
- sys.executable, self.fixup_path, '--output-dir=' + self.working_dir,
- input_path
- ])
-
- def TestText(self, input_filename, input_root, expected_txt_path, pdf_path):
- txt_path = os.path.join(self.working_dir, input_root + '.txt')
-
- with open(txt_path, 'w') as outfile:
- cmd_to_run = [
- self.pdfium_test_path, '--send-events', '--time=' + TEST_SEED_TIME
- ]
-
- if self.options.disable_javascript:
- cmd_to_run.append('--disable-javascript')
-
- if self.options.disable_xfa:
- cmd_to_run.append('--disable-xfa')
-
- cmd_to_run.append(pdf_path)
- subprocess.check_call(cmd_to_run, stdout=outfile)
-
- # If the expected file does not exist, the output is expected to be empty.
- if not os.path.exists(expected_txt_path):
- return self._VerifyEmptyText(txt_path)
-
- # If JavaScript is disabled, the output should be empty.
- # However, if the test is suppressed and JavaScript is disabled, do not
- # verify that the text is empty so the suppressed test does not surprise.
- if (self.options.disable_javascript and
- not self.test_suppressor.IsResultSuppressed(input_filename)):
- return self._VerifyEmptyText(txt_path)
-
- cmd = [sys.executable, self.text_diff_path, expected_txt_path, txt_path]
- return common.RunCommand(cmd)
-
- def _VerifyEmptyText(self, txt_path):
- try:
- with open(txt_path, "r") as txt_file:
- txt_data = txt_file.readlines()
- if not len(txt_data):
- return None
- sys.stdout.write('Unexpected output:\n')
- for line in txt_data:
- sys.stdout.write(line)
- raise Exception('%s should be empty.' % txt_path)
- except Exception as e:
- return e
-
- def TestPixel(self, pdf_path, use_ahem):
- cmd_to_run = [
- self.pdfium_test_path, '--send-events', '--png', '--md5',
- '--time=' + TEST_SEED_TIME
- ]
-
- if self.oneshot_renderer:
- cmd_to_run.append('--render-oneshot')
-
- if use_ahem:
- cmd_to_run.append('--font-dir=%s' % self.font_dir)
-
- if self.options.disable_javascript:
- cmd_to_run.append('--disable-javascript')
-
- if self.options.disable_xfa:
- cmd_to_run.append('--disable-xfa')
-
- if self.options.reverse_byte_order:
- cmd_to_run.append('--reverse-byte-order')
-
- cmd_to_run.append(pdf_path)
- return common.RunCommandExtractHashedFiles(cmd_to_run)
-
- def HandleResult(self, input_filename, input_path, result):
- success, image_paths = result
-
- if image_paths:
- for img_path, md5_hash in image_paths:
- # The output filename without image extension becomes the test name.
- # For example, "/path/to/.../testing/corpus/example_005.pdf.0.png"
- # becomes "example_005.pdf.0".
- test_name = os.path.splitext(os.path.split(img_path)[1])[0]
-
- matched = "suppressed"
- if not self.test_suppressor.IsResultSuppressed(input_filename):
- matched = self.gold_baseline.MatchLocalResult(test_name, md5_hash)
- if matched == gold.GoldBaseline.MISMATCH:
- print 'Skia Gold hash mismatch for test case: %s' % test_name
- elif matched == gold.GoldBaseline.NO_BASELINE:
- print 'No Skia Gold baseline found for test case: %s' % test_name
-
- if self.gold_results:
- self.gold_results.AddTestResult(test_name, md5_hash, img_path,
- matched)
-
- if self.test_suppressor.IsResultSuppressed(input_filename):
+ test_result.status = self._SuppressStatus(input_filename,
+ test_result.status)
+ if test_result.status == result_types.UNKNOWN:
self.result_suppressed_cases.append(input_filename)
- if success:
- self.surprises.append(input_path)
- else:
- if not success:
- self.failures.append(input_path)
+ self.surprises.append(test_case.input_path)
+ elif test_result.status == result_types.SKIP:
+ self.result_suppressed_cases.append(input_filename)
+ elif not test_result.IsPass():
+ self.failures.append(test_case.input_path)
+
+ for artifact in test_result.image_artifacts:
+ if artifact.skia_gold_status == result_types.PASS:
+ if self.IsResultSuppressed(artifact.image_path):
+ self.skia_gold_unexpected_successes.append(artifact.GetSkiaGoldId())
+ else:
+ self.skia_gold_successes.append(artifact.GetSkiaGoldId())
+ elif artifact.skia_gold_status == result_types.FAIL:
+ self.skia_gold_failures.append(artifact.GetSkiaGoldId())
+
+ # Log test result.
+ print(f'{test_result.status}: {test_result.test_id}')
+ if not test_result.IsPass():
+ if test_result.reason:
+ print(f'Failure reason: {test_result.reason}')
+ if test_result.log:
+ decoded_log = bytes.decode(test_result.log, errors='backslashreplace')
+ print(f'Test output:\n{decoded_log}')
+ for artifact in test_result.image_artifacts:
+ if artifact.skia_gold_status == result_types.FAIL:
+ print(f'Failed Skia Gold: {artifact.image_path}')
+ if artifact.image_diff:
+ print(f'Failed image diff: {artifact.image_diff.reason}')
+
+ # Report test result to ResultDB.
+ if self.resultdb:
+ only_artifacts = None
+ only_failure_reason = test_result.reason
+ if len(test_result.image_artifacts) == 1:
+ only = test_result.image_artifacts[0]
+ only_artifacts = only.GetDiffArtifacts()
+ if only.GetDiffReason():
+ only_failure_reason += f': {only.GetDiffReason()}'
+ self.resultdb.Post(
+ test_id=test_result.test_id,
+ status=test_result.status,
+ duration=test_result.duration_milliseconds,
+ test_log=test_result.log,
+ test_file=None,
+ artifacts=only_artifacts,
+ failure_reason=only_failure_reason)
+
+ # Milo only supports a single diff per test, so if we have multiple pages,
+ # report each page as its own "test."
+ if len(test_result.image_artifacts) > 1:
+ for page, artifact in enumerate(test_result.image_artifacts):
+ self.resultdb.Post(
+ test_id=f'{test_result.test_id}/{page}',
+ status=self._SuppressArtifactStatus(test_result,
+ artifact.GetDiffStatus()),
+ duration=None,
+ test_log=None,
+ test_file=None,
+ artifacts=artifact.GetDiffArtifacts(),
+ failure_reason=artifact.GetDiffReason())
+
+ def _SuppressStatus(self, input_filename, status):
+ if not self.IsResultSuppressed(input_filename):
+ return status
+
+ if status == result_types.PASS:
+ # There isn't an actual status for succeeded-but-ignored, so use the
+ # "abort" status to differentiate this from failed-but-ignored.
+ #
+ # Note that this appears as a preliminary failure in Gerrit.
+ return result_types.UNKNOWN
+
+ # There isn't an actual status for failed-but-ignored, so use the "skip"
+ # status to differentiate this from succeeded-but-ignored.
+ return result_types.SKIP
+
+ def _SuppressArtifactStatus(self, test_result, status):
+ if status != result_types.FAIL:
+ return status
+
+ if test_result.status != result_types.SKIP:
+ return status
+
+ return result_types.SKIP
def Run(self):
# Running a test defines a number of attributes on the fly.
# pylint: disable=attribute-defined-outside-init
- parser = optparse.OptionParser()
+ relative_test_dir = self.per_process_config.test_dir
+ if relative_test_dir != 'corpus':
+ relative_test_dir = os.path.join('resources', relative_test_dir)
- parser.add_option(
+ parser = argparse.ArgumentParser()
+
+ parser.add_argument(
'--build-dir',
default=os.path.join('out', 'Debug'),
help='relative path from the base source directory')
- parser.add_option(
+ parser.add_argument(
'-j',
default=multiprocessing.cpu_count(),
dest='num_workers',
- type='int',
+ type=int,
help='run NUM_WORKERS jobs in parallel')
- parser.add_option(
+ parser.add_argument(
'--disable-javascript',
- action="store_true",
- dest="disable_javascript",
+ action='store_true',
help='Prevents JavaScript from executing in PDF files.')
- parser.add_option(
+ parser.add_argument(
'--disable-xfa',
- action="store_true",
- dest="disable_xfa",
+ action='store_true',
help='Prevents processing XFA forms.')
- parser.add_option(
+ parser.add_argument(
+ '--render-oneshot',
+ action='store_true',
+ help='Sets whether to use the oneshot renderer.')
+
+ parser.add_argument(
+ '--run-skia-gold',
+ action='store_true',
+ default=False,
+ help='When flag is on, skia gold tests will be run.')
+
+ # TODO: Remove when pdfium recipe stops passing this argument
+ parser.add_argument(
'--gold_properties',
default='',
- dest="gold_properties",
- help='Key value pairs that are written to the top level '
- 'of the JSON file that is ingested by Gold.')
+ help='Key value pairs that are written to the top level of the JSON '
+ 'file that is ingested by Gold.')
- parser.add_option(
- '--gold_key',
- default='',
- dest="gold_key",
- help='Key value pairs that are added to the "key" field '
- 'of the JSON file that is ingested by Gold.')
-
- parser.add_option(
- '--gold_output_dir',
- default='',
- dest="gold_output_dir",
- help='Path of where to write the JSON output to be '
- 'uploaded to Gold.')
-
- parser.add_option(
+ # TODO: Remove when pdfium recipe stops passing this argument
+ parser.add_argument(
'--gold_ignore_hashes',
default='',
- dest="gold_ignore_hashes",
help='Path to a file with MD5 hashes we wish to ignore.')
- parser.add_option(
+ parser.add_argument(
'--regenerate_expected',
- default='',
- dest="regenerate_expected",
- help='Regenerates expected images. Valid values are '
- '"all" to regenerate all expected pngs, and '
- '"platform" to regenerate only platform-specific '
- 'expected pngs.')
+ action='store_true',
+ help='Regenerates expected images. For each failing image diff, this '
+ 'will regenerate the most specific expected image file that exists. '
+ 'This also will suggest removals of unnecessary expected image files '
+ 'by renaming them with an additional ".bak" extension, although these '
+ 'removals should be reviewed manually. Use "git clean" to quickly deal '
+ 'with any ".bak" files.')
- parser.add_option(
+ parser.add_argument(
'--reverse-byte-order',
action='store_true',
- dest="reverse_byte_order",
help='Run image-based tests using --reverse-byte-order.')
- parser.add_option(
+ parser.add_argument(
'--ignore_errors',
- action="store_true",
- dest="ignore_errors",
+ action='store_true',
help='Prevents the return value from being non-zero '
'when image comparison fails.')
- self.options, self.args = parser.parse_args()
+ parser.add_argument(
+ 'inputted_file_paths',
+ nargs='*',
+ help='Path to test files to run, relative to '
+ f'testing/{relative_test_dir}. If omitted, runs all test files under '
+ f'testing/{relative_test_dir}.',
+ metavar='relative/test/path')
- if (self.options.regenerate_expected and
- self.options.regenerate_expected not in ['all', 'platform']):
- print 'FAILURE: --regenerate_expected must be "all" or "platform"'
+ skia_gold.add_skia_gold_args(parser)
+
+ self.per_process_config.options = parser.parse_args()
+
+ finder = self.per_process_config.NewFinder()
+ pdfium_test_path = self.per_process_config.GetPdfiumTestPath(finder)
+ if not os.path.exists(pdfium_test_path):
+ print(f"FAILURE: Can't find test executable '{pdfium_test_path}'")
+ print('Use --build-dir to specify its location.')
return 1
+ self.per_process_config.InitializeFeatures(pdfium_test_path)
- finder = common.DirectoryFinder(self.options.build_dir)
- self.fixup_path = finder.ScriptPath('fixup_pdf_template.py')
- self.text_diff_path = finder.ScriptPath('text_diff.py')
- self.font_dir = os.path.join(finder.TestingDir(), 'resources', 'fonts')
+ self.per_process_state = _PerProcessState(self.per_process_config)
+ shutil.rmtree(self.per_process_state.working_dir, ignore_errors=True)
+ os.makedirs(self.per_process_state.working_dir)
- self.source_dir = finder.TestingDir()
- if self.test_dir != 'corpus':
- test_dir = finder.TestingDir(os.path.join('resources', self.test_dir))
- else:
- test_dir = finder.TestingDir(self.test_dir)
-
- self.pdfium_test_path = finder.ExecutablePath('pdfium_test')
- if not os.path.exists(self.pdfium_test_path):
- print "FAILURE: Can't find test executable '%s'" % self.pdfium_test_path
- print 'Use --build-dir to specify its location.'
- return 1
-
- self.working_dir = finder.WorkingDir(os.path.join('testing', self.test_dir))
- shutil.rmtree(self.working_dir, ignore_errors=True)
- os.makedirs(self.working_dir)
-
- self.feature_string = subprocess.check_output(
- [self.pdfium_test_path, '--show-config'])
- self.test_suppressor = suppressor.Suppressor(
- finder, self.feature_string, self.options.disable_javascript,
- self.options.disable_xfa)
- self.image_differ = pngdiffer.PNGDiffer(finder,
- self.options.reverse_byte_order)
- error_message = self.image_differ.CheckMissingTools(
+ error_message = self.per_process_state.image_differ.CheckMissingTools(
self.options.regenerate_expected)
if error_message:
- print "FAILURE: %s" % error_message
+ print('FAILURE:', error_message)
return 1
- self.gold_baseline = gold.GoldBaseline(self.options.gold_properties)
+ self.resultdb = result_sink.TryInitClient()
+ if self.resultdb:
+ print('Detected ResultSink environment')
- walk_from_dir = finder.TestingDir(test_dir)
+ # Collect test cases.
+ walk_from_dir = finder.TestingDir(relative_test_dir)
- self.test_cases = []
+ self.test_cases = TestCaseManager()
self.execution_suppressed_cases = []
input_file_re = re.compile('^.+[.](in|pdf)$')
- if self.args:
- for file_name in self.args:
- file_name.replace('.pdf', '.in')
+ if self.options.inputted_file_paths:
+ for file_name in self.options.inputted_file_paths:
input_path = os.path.join(walk_from_dir, file_name)
if not os.path.isfile(input_path):
- print "Can't find test file '%s'" % file_name
+ print(f"Can't find test file '{file_name}'")
return 1
- self.test_cases.append((os.path.basename(input_path),
- os.path.dirname(input_path)))
+ self.test_cases.NewTestCase(input_path)
else:
for file_dir, _, filename_list in os.walk(walk_from_dir):
for input_filename in filename_list:
if input_file_re.match(input_filename):
input_path = os.path.join(file_dir, input_filename)
- if self.test_suppressor.IsExecutionSuppressed(input_path):
+ if self.IsExecutionSuppressed(input_path):
self.execution_suppressed_cases.append(input_path)
- else:
- if os.path.isfile(input_path):
- self.test_cases.append((input_filename, file_dir))
+ continue
+ if not os.path.isfile(input_path):
+ continue
- self.test_cases.sort()
+ self.test_cases.NewTestCase(input_path)
+
+ # Execute test cases.
self.failures = []
self.surprises = []
+ self.skia_gold_successes = []
+ self.skia_gold_unexpected_successes = []
+ self.skia_gold_failures = []
self.result_suppressed_cases = []
- # Collect Gold results if an output directory was named.
- self.gold_results = None
- if self.options.gold_output_dir:
- self.gold_results = gold.GoldResults(
- self.test_type, self.options.gold_output_dir,
- self.options.gold_properties, self.options.gold_key,
- self.options.gold_ignore_hashes)
+ if self.IsSkiaGoldEnabled():
+ assert self.options.gold_output_dir
+ # Clear out and create top level gold output directory before starting
+ skia_gold.clear_gold_output_dir(self.options.gold_output_dir)
- if self.options.num_workers > 1 and len(self.test_cases) > 1:
- try:
- pool = multiprocessing.Pool(self.options.num_workers)
- worker_func = functools.partial(TestOneFileParallel, self)
+ with multiprocessing.Pool(
+ processes=self.options.num_workers,
+ initializer=_InitializePerProcessState,
+ initargs=[self.per_process_config]) as pool:
+ if self.per_process_config.test_type in TEXT_TESTS:
+ test_function = _RunTextTest
+ else:
+ test_function = _RunPixelTest
+ for result in pool.imap(test_function, self.test_cases):
+ self.HandleResult(self.test_cases.GetTestCase(result.test_id), result)
- worker_results = pool.imap(worker_func, self.test_cases)
- for worker_result in worker_results:
- result, input_filename, source_dir = worker_result
- input_path = os.path.join(source_dir, input_filename)
-
- self.HandleResult(input_filename, input_path, result)
-
- except KeyboardInterrupt:
- pool.terminate()
- finally:
- pool.close()
- pool.join()
- else:
- for test_case in self.test_cases:
- input_filename, input_file_dir = test_case
- result = self.GenerateAndTest(input_filename, input_file_dir)
- self.HandleResult(input_filename,
- os.path.join(input_file_dir, input_filename), result)
-
- if self.gold_results:
- self.gold_results.WriteResults()
-
+ # Report test results.
if self.surprises:
self.surprises.sort()
- print '\n\nUnexpected Successes:'
+ print('\nUnexpected Successes:')
for surprise in self.surprises:
- print surprise
+ print(surprise)
if self.failures:
self.failures.sort()
- print '\n\nSummary of Failures:'
+ print('\nSummary of Failures:')
for failure in self.failures:
- print failure
+ print(failure)
+
+ if self.skia_gold_unexpected_successes:
+ self.skia_gold_unexpected_successes.sort()
+ print('\nUnexpected Skia Gold Successes:')
+ for surprise in self.skia_gold_unexpected_successes:
+ print(surprise)
+
+ if self.skia_gold_failures:
+ self.skia_gold_failures.sort()
+ print('\nSummary of Skia Gold Failures:')
+ for failure in self.skia_gold_failures:
+ print(failure)
self._PrintSummary()
@@ -462,23 +358,551 @@
number_suppressed = len(self.result_suppressed_cases)
number_successes = number_test_cases - number_failures - number_suppressed
number_surprises = len(self.surprises)
- print
- print 'Test cases executed: %d' % number_test_cases
- print ' Successes: %d' % number_successes
- print ' Suppressed: %d' % number_suppressed
- print ' Surprises: %d' % number_surprises
- print ' Failures: %d' % number_failures
- print
- print 'Test cases not executed: %d' % len(self.execution_suppressed_cases)
+ print('\nTest cases executed:', number_test_cases)
+ print(' Successes:', number_successes)
+ print(' Suppressed:', number_suppressed)
+ print(' Surprises:', number_surprises)
+ print(' Failures:', number_failures)
+ if self.IsSkiaGoldEnabled():
+ number_gold_failures = len(self.skia_gold_failures)
+ number_gold_successes = len(self.skia_gold_successes)
+ number_gold_surprises = len(self.skia_gold_unexpected_successes)
+ number_total_gold_tests = sum(
+ [number_gold_failures, number_gold_successes, number_gold_surprises])
+ print('\nSkia Gold Test cases executed:', number_total_gold_tests)
+ print(' Skia Gold Successes:', number_gold_successes)
+ print(' Skia Gold Surprises:', number_gold_surprises)
+ print(' Skia Gold Failures:', number_gold_failures)
+ skia_tester = self.per_process_state.GetSkiaGoldTester()
+ if self.skia_gold_failures and skia_tester.IsTryjobRun():
+ cl_triage_link = skia_tester.GetCLTriageLink()
+ print(' Triage link for CL:', cl_triage_link)
+ skia_tester.WriteCLTriageLink(cl_triage_link)
+ print()
+ print('Test cases not executed:', len(self.execution_suppressed_cases))
def SetDeleteOutputOnSuccess(self, new_value):
"""Set whether to delete generated output if the test passes."""
- self.delete_output_on_success = new_value
+ self.per_process_config.delete_output_on_success = new_value
def SetEnforceExpectedImages(self, new_value):
"""Set whether to enforce that each test case provide an expected image."""
- self.enforce_expected_images = new_value
+ self.per_process_config.enforce_expected_images = new_value
- def SetOneShotRenderer(self, new_value):
- """Set whether to use the oneshot renderer. """
- self.oneshot_renderer = new_value
+
+def _RunTextTest(test_case):
+ """Runs a text test case."""
+ test_case_runner = _TestCaseRunner(test_case)
+ with test_case_runner:
+ test_case_runner.test_result = test_case_runner.GenerateAndTest(
+ test_case_runner.TestText)
+ return test_case_runner.test_result
+
+
+def _RunPixelTest(test_case):
+ """Runs a pixel test case."""
+ test_case_runner = _TestCaseRunner(test_case)
+ with test_case_runner:
+ test_case_runner.test_result = test_case_runner.GenerateAndTest(
+ test_case_runner.TestPixel)
+ return test_case_runner.test_result
+
+
+# `_PerProcessState` singleton. This is initialized when creating the
+# `multiprocessing.Pool()`. `TestRunner.Run()` creates its own separate
+# instance of `_PerProcessState` as well.
+_per_process_state = None
+
+
+def _InitializePerProcessState(config):
+ """Initializes the `_per_process_state` singleton."""
+ global _per_process_state
+ assert not _per_process_state
+ _per_process_state = _PerProcessState(config)
+
+
+@dataclass
+class _PerProcessConfig:
+ """Configuration for initializing `_PerProcessState`.
+
+ Attributes:
+ test_dir: The name of the test directory.
+ test_type: The test type.
+ delete_output_on_success: Whether to delete output on success.
+ enforce_expected_images: Whether to enforce expected images.
+ options: The dictionary of command line options.
+ features: The list of features supported by `pdfium_test`.
+ """
+ test_dir: str
+ test_type: str
+ delete_output_on_success: bool = False
+ enforce_expected_images: bool = False
+ options: dict = None
+ features: list = None
+
+ def NewFinder(self):
+ return common.DirectoryFinder(self.options.build_dir)
+
+ def GetPdfiumTestPath(self, finder):
+ return finder.ExecutablePath('pdfium_test')
+
+ def InitializeFeatures(self, pdfium_test_path):
+ output = subprocess.check_output([pdfium_test_path, '--show-config'],
+ timeout=TEST_TIMEOUT)
+ self.features = output.decode('utf-8').strip().split(',')
+
+
+class _PerProcessState:
+ """State defined per process."""
+
+ def __init__(self, config):
+ self.test_dir = config.test_dir
+ self.test_type = config.test_type
+ self.delete_output_on_success = config.delete_output_on_success
+ self.enforce_expected_images = config.enforce_expected_images
+ self.options = config.options
+ self.features = config.features
+
+ finder = config.NewFinder()
+ self.pdfium_test_path = config.GetPdfiumTestPath(finder)
+ self.fixup_path = finder.ScriptPath('fixup_pdf_template.py')
+ self.text_diff_path = finder.ScriptPath('text_diff.py')
+ self.font_dir = os.path.join(finder.TestingDir(), 'resources', 'fonts')
+ self.third_party_font_dir = finder.ThirdPartyFontsDir()
+
+ self.source_dir = finder.TestingDir()
+ self.working_dir = finder.WorkingDir(os.path.join('testing', self.test_dir))
+
+ self.test_suppressor = suppressor.Suppressor(
+ finder, self.features, self.options.disable_javascript,
+ self.options.disable_xfa)
+ self.image_differ = pngdiffer.PNGDiffer(finder, self.features,
+ self.options.reverse_byte_order)
+
+ self.process_name = multiprocessing.current_process().name
+ self.skia_tester = None
+
+ def __getstate__(self):
+ raise RuntimeError('Cannot pickle per-process state')
+
+ def GetSkiaGoldTester(self):
+ """Gets the `SkiaGoldTester` singleton for this worker."""
+ if not self.skia_tester:
+ self.skia_tester = skia_gold.SkiaGoldTester(
+ source_type=self.test_type,
+ skia_gold_args=self.options,
+ process_name=self.process_name)
+ return self.skia_tester
+
+
+class _TestCaseRunner:
+ """Runner for a single test case."""
+
+ def __init__(self, test_case):
+ self.test_case = test_case
+ self.test_result = None
+ self.duration_start = 0
+
+ self.source_dir, self.input_filename = os.path.split(
+ self.test_case.input_path)
+ self.pdf_path = os.path.join(self.working_dir, f'{self.test_id}.pdf')
+ self.actual_images = None
+
+ def __enter__(self):
+ self.duration_start = time.perf_counter_ns()
+ return self
+
+ def __exit__(self, exc_type, exc_value, traceback):
+ if not self.test_result:
+ self.test_result = self.test_case.NewResult(
+ result_types.UNKNOWN, reason='No test result recorded')
+ duration = time.perf_counter_ns() - self.duration_start
+ self.test_result.duration_milliseconds = duration * 1e-6
+
+ @property
+ def options(self):
+ return _per_process_state.options
+
+ @property
+ def test_id(self):
+ return self.test_case.test_id
+
+ @property
+ def working_dir(self):
+ return _per_process_state.working_dir
+
+ def IsResultSuppressed(self):
+ return _per_process_state.test_suppressor.IsResultSuppressed(
+ self.input_filename)
+
+ def IsImageDiffSuppressed(self):
+ return _per_process_state.test_suppressor.IsImageDiffSuppressed(
+ self.input_filename)
+
+ def GetImageMatchingAlgorithm(self):
+ return _per_process_state.test_suppressor.GetImageMatchingAlgorithm(
+ self.input_filename)
+
+ def RunCommand(self, command, stdout=None):
+ """Runs a test command.
+
+ Args:
+ command: The list of command arguments.
+ stdout: Optional `file`-like object to send standard output.
+
+ Returns:
+ The test result.
+ """
+
+ # Standard output and error are directed to the test log. If `stdout` was
+ # provided, redirect standard output to it instead.
+ if stdout:
+ assert stdout != subprocess.PIPE
+ try:
+ stdout.fileno()
+ except OSError:
+ # `stdout` doesn't have a file descriptor, so it can't be passed to
+ # `subprocess.run()` directly.
+ original_stdout = stdout
+ stdout = subprocess.PIPE
+ stderr = subprocess.PIPE
+ else:
+ stdout = subprocess.PIPE
+ stderr = subprocess.STDOUT
+
+ test_result = self.test_case.NewResult(result_types.PASS)
+ try:
+ run_result = subprocess.run(
+ command,
+ stdout=stdout,
+ stderr=stderr,
+ timeout=TEST_TIMEOUT,
+ check=False)
+ if run_result.returncode != 0:
+ test_result.status = result_types.FAIL
+ test_result.reason = 'Command {} exited with code {}'.format(
+ run_result.args, run_result.returncode)
+ except subprocess.TimeoutExpired as timeout_expired:
+ run_result = timeout_expired
+ test_result.status = result_types.TIMEOUT
+ test_result.reason = 'Command {} timed out'.format(run_result.cmd)
+
+ if stdout == subprocess.PIPE and stderr == subprocess.PIPE:
+ # Copy captured standard output, if any, to the original `stdout`.
+ if run_result.stdout:
+ original_stdout.write(run_result.stdout)
+
+ if not test_result.IsPass():
+ # On failure, report captured output to the test log.
+ if stderr == subprocess.STDOUT:
+ test_result.log = run_result.stdout
+ else:
+ test_result.log = run_result.stderr
+ return test_result
+
+ def GenerateAndTest(self, test_function):
+ """Generate test input and run pdfium_test."""
+ test_result = self.Generate()
+ if not test_result.IsPass():
+ return test_result
+
+ return test_function()
+
+ def _RegenerateIfNeeded(self):
+ if not self.options.regenerate_expected:
+ return
+ if self.IsResultSuppressed() or self.IsImageDiffSuppressed():
+ return
+ _per_process_state.image_differ.Regenerate(
+ self.input_filename,
+ self.source_dir,
+ self.working_dir,
+ image_matching_algorithm=self.GetImageMatchingAlgorithm())
+
+ def Generate(self):
+ input_event_path = os.path.join(self.source_dir, f'{self.test_id}.evt')
+ if os.path.exists(input_event_path):
+ output_event_path = f'{os.path.splitext(self.pdf_path)[0]}.evt'
+ shutil.copyfile(input_event_path, output_event_path)
+
+ template_path = os.path.join(self.source_dir, f'{self.test_id}.in')
+ if not os.path.exists(template_path):
+ if os.path.exists(self.test_case.input_path):
+ shutil.copyfile(self.test_case.input_path, self.pdf_path)
+ return self.test_case.NewResult(result_types.PASS)
+
+ return self.RunCommand([
+ sys.executable, _per_process_state.fixup_path,
+ f'--output-dir={self.working_dir}', template_path
+ ])
+
+ def TestText(self):
+ txt_path = os.path.join(self.working_dir, f'{self.test_id}.txt')
+ with open(txt_path, 'w') as outfile:
+ cmd_to_run = [
+ _per_process_state.pdfium_test_path, '--send-events',
+ f'--time={TEST_SEED_TIME}'
+ ]
+
+ if self.options.disable_javascript:
+ cmd_to_run.append('--disable-javascript')
+
+ if self.options.disable_xfa:
+ cmd_to_run.append('--disable-xfa')
+
+ cmd_to_run.append(self.pdf_path)
+ test_result = self.RunCommand(cmd_to_run, stdout=outfile)
+ if not test_result.IsPass():
+ return test_result
+
+ # If the expected file does not exist, the output is expected to be empty.
+ expected_txt_path = os.path.join(self.source_dir,
+ f'{self.test_id}_expected.txt')
+ if not os.path.exists(expected_txt_path):
+ return self._VerifyEmptyText(txt_path)
+
+ # If JavaScript is disabled, the output should be empty.
+ # However, if the test is suppressed and JavaScript is disabled, do not
+ # verify that the text is empty so the suppressed test does not surprise.
+ if self.options.disable_javascript and not self.IsResultSuppressed():
+ return self._VerifyEmptyText(txt_path)
+
+ return self.RunCommand([
+ sys.executable, _per_process_state.text_diff_path, expected_txt_path,
+ txt_path
+ ])
+
+ def _VerifyEmptyText(self, txt_path):
+ with open(txt_path, "rb") as txt_file:
+ txt_data = txt_file.read()
+
+ if txt_data:
+ return self.test_case.NewResult(
+ result_types.FAIL, log=txt_data, reason=f'{txt_path} should be empty')
+
+ return self.test_case.NewResult(result_types.PASS)
+
+ # TODO(crbug.com/pdfium/1656): Remove when ready to fully switch over to
+ # Skia Gold
+ def TestPixel(self):
+ # Remove any existing generated images from previous runs.
+ self.actual_images = _per_process_state.image_differ.GetActualFiles(
+ self.input_filename, self.source_dir, self.working_dir)
+ self._CleanupPixelTest()
+
+ # Generate images.
+ cmd_to_run = [
+ _per_process_state.pdfium_test_path, '--send-events', '--png', '--md5',
+ f'--time={TEST_SEED_TIME}'
+ ]
+
+ if 'use_ahem' in self.source_dir or 'use_symbolneu' in self.source_dir:
+ cmd_to_run.append(f'--font-dir={_per_process_state.font_dir}')
+ else:
+ cmd_to_run.append(f'--font-dir={_per_process_state.third_party_font_dir}')
+ cmd_to_run.append('--croscore-font-names')
+
+ if self.options.disable_javascript:
+ cmd_to_run.append('--disable-javascript')
+
+ if self.options.disable_xfa:
+ cmd_to_run.append('--disable-xfa')
+
+ if self.options.render_oneshot:
+ cmd_to_run.append('--render-oneshot')
+
+ if self.options.reverse_byte_order:
+ cmd_to_run.append('--reverse-byte-order')
+
+ cmd_to_run.append(self.pdf_path)
+
+ with BytesIO() as command_output:
+ test_result = self.RunCommand(cmd_to_run, stdout=command_output)
+ if not test_result.IsPass():
+ return test_result
+
+ test_result.image_artifacts = []
+ for line in command_output.getvalue().splitlines():
+ # Expect this format: MD5:<path to image file>:<hexadecimal MD5 hash>
+ line = bytes.decode(line).strip()
+ if line.startswith('MD5:'):
+ image_path, md5_hash = line[4:].rsplit(':', 1)
+ test_result.image_artifacts.append(
+ self._NewImageArtifact(
+ image_path=image_path.strip(), md5_hash=md5_hash.strip()))
+
+ if self.actual_images:
+ image_diffs = _per_process_state.image_differ.ComputeDifferences(
+ self.input_filename,
+ self.source_dir,
+ self.working_dir,
+ image_matching_algorithm=self.GetImageMatchingAlgorithm())
+ if image_diffs:
+ test_result.status = result_types.FAIL
+ test_result.reason = 'Images differ'
+
+ # Merge image diffs into test result.
+ diff_map = {}
+ diff_log = []
+ for diff in image_diffs:
+ diff_map[diff.actual_path] = diff
+ diff_log.append(f'{os.path.basename(diff.actual_path)} vs. ')
+ if diff.expected_path:
+ diff_log.append(f'{os.path.basename(diff.expected_path)}\n')
+ else:
+ diff_log.append('missing expected file\n')
+
+ for artifact in test_result.image_artifacts:
+ artifact.image_diff = diff_map.get(artifact.image_path)
+ test_result.log = ''.join(diff_log).encode()
+
+ elif _per_process_state.enforce_expected_images:
+ if not self.IsImageDiffSuppressed():
+ test_result.status = result_types.FAIL
+ test_result.reason = 'Missing expected images'
+
+ if not test_result.IsPass():
+ self._RegenerateIfNeeded()
+ return test_result
+
+ if _per_process_state.delete_output_on_success:
+ self._CleanupPixelTest()
+ return test_result
+
+ def _NewImageArtifact(self, *, image_path, md5_hash):
+ artifact = ImageArtifact(image_path=image_path, md5_hash=md5_hash)
+
+ if self.options.run_skia_gold:
+ if _per_process_state.GetSkiaGoldTester().UploadTestResultToSkiaGold(
+ artifact.GetSkiaGoldId(), artifact.image_path):
+ artifact.skia_gold_status = result_types.PASS
+ else:
+ artifact.skia_gold_status = result_types.FAIL
+
+ return artifact
+
+ def _CleanupPixelTest(self):
+ for image_file in self.actual_images:
+ if os.path.exists(image_file):
+ os.remove(image_file)
+
+
+@dataclass
+class TestCase:
+ """Description of a test case to run.
+
+ Attributes:
+ test_id: A unique identifier for the test.
+ input_path: The absolute path to the test file.
+ """
+ test_id: str
+ input_path: str
+
+ def NewResult(self, status, **kwargs):
+ """Derives a new test result corresponding to this test case."""
+ return TestResult(test_id=self.test_id, status=status, **kwargs)
+
+
+@dataclass
+class TestResult:
+ """Results from running a test case.
+
+ Attributes:
+ test_id: The corresponding test case ID.
+ status: The overall `result_types` status.
+ duration_milliseconds: Test time in milliseconds.
+ log: Optional log of the test's output.
+ image_artfacts: Optional list of image artifacts.
+ reason: Optional reason why the test failed.
+ """
+ test_id: str
+ status: str
+ duration_milliseconds: float = None
+ log: str = None
+ image_artifacts: list = field(default_factory=list)
+ reason: str = None
+
+ def IsPass(self):
+ """Whether the test passed."""
+ return self.status == result_types.PASS
+
+
+@dataclass
+class ImageArtifact:
+ """Image artifact for a test result.
+
+ Attributes:
+ image_path: The absolute path to the image file.
+ md5_hash: The MD5 hash of the pixel buffer.
+ skia_gold_status: Optional Skia Gold status.
+ image_diff: Optional image diff.
+ """
+ image_path: str
+ md5_hash: str
+ skia_gold_status: str = None
+ image_diff: pngdiffer.ImageDiff = None
+
+ def GetSkiaGoldId(self):
+ # The output filename without image extension becomes the test ID. For
+ # example, "/path/to/.../testing/corpus/example_005.pdf.0.png" becomes
+ # "example_005.pdf.0".
+ return _GetTestId(os.path.basename(self.image_path))
+
+ def GetDiffStatus(self):
+ return result_types.FAIL if self.image_diff else result_types.PASS
+
+ def GetDiffReason(self):
+ return self.image_diff.reason if self.image_diff else None
+
+ def GetDiffArtifacts(self):
+ if not self.image_diff:
+ return None
+ if not self.image_diff.expected_path or not self.image_diff.diff_path:
+ return None
+ return {
+ 'actual_image':
+ _GetArtifactFromFilePath(self.image_path),
+ 'expected_image':
+ _GetArtifactFromFilePath(self.image_diff.expected_path),
+ 'image_diff':
+ _GetArtifactFromFilePath(self.image_diff.diff_path)
+ }
+
+
+class TestCaseManager:
+ """Manages a collection of test cases."""
+
+ def __init__(self):
+ self.test_cases = {}
+
+ def __len__(self):
+ return len(self.test_cases)
+
+ def __iter__(self):
+ return iter(self.test_cases.values())
+
+ def NewTestCase(self, input_path, **kwargs):
+ """Creates and registers a new test case."""
+ input_basename = os.path.basename(input_path)
+ test_id = _GetTestId(input_basename)
+ if test_id in self.test_cases:
+ raise ValueError(
+ f'Test ID "{test_id}" derived from "{input_basename}" must be unique')
+
+ test_case = TestCase(test_id=test_id, input_path=input_path, **kwargs)
+ self.test_cases[test_id] = test_case
+ return test_case
+
+ def GetTestCase(self, test_id):
+ """Looks up a test case previously registered by `NewTestCase()`."""
+ return self.test_cases[test_id]
+
+
+def _GetTestId(input_basename):
+ """Constructs a test ID by stripping the last extension from the basename."""
+ return os.path.splitext(input_basename)[0]
+
+
+def _GetArtifactFromFilePath(file_path):
+ """Constructs a ResultSink artifact from a file path."""
+ return {'filePath': file_path}
diff --git a/testing/tools/text_diff.py b/testing/tools/text_diff.py
index fdf45a0..7dcb1c2 100755
--- a/testing/tools/text_diff.py
+++ b/testing/tools/text_diff.py
@@ -1,5 +1,5 @@
-#!/usr/bin/env python
-# Copyright 2015 The PDFium Authors. All rights reserved.
+#!/usr/bin/env python3
+# Copyright 2015 The PDFium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
@@ -9,7 +9,7 @@
def main(argv):
if len(argv) != 3:
- print '%s: invalid arguments' % argv[0]
+ print('%s: invalid arguments' % argv[0])
return 2
filename1 = argv[1]
filename2 = argv[2]
@@ -21,7 +21,7 @@
diffs = difflib.unified_diff(
str1, str2, fromfile=filename1, tofile=filename2)
except Exception as e:
- print "something went astray: %s" % e
+ print("something went astray: %s" % e)
return 1
status_code = 0
for diff in diffs:
diff --git a/testing/unit_test_main.cpp b/testing/unit_test_main.cpp
index 5d50249..16e8f0f 100644
--- a/testing/unit_test_main.cpp
+++ b/testing/unit_test_main.cpp
@@ -1,57 +1,38 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include <memory>
-#include <string>
-
#include "core/fxcrt/fx_memory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "testing/test_support.h"
+#include "testing/pdf_test_environment.h"
#ifdef PDF_ENABLE_V8
-#include "testing/v8_initializer.h"
-#include "v8/include/v8-platform.h"
-#include "v8/include/v8.h"
-#endif // PDF_ENABLE_V8
-
+#include "testing/v8_test_environment.h"
#ifdef PDF_ENABLE_XFA
-#include "testing/xfa_unit_test_support.h"
+#include "testing/xfa_test_environment.h"
#endif // PDF_ENABLE_XFA
+#endif // PDF_ENABLE_V8
// Can't use gtest-provided main since we need to initialize partition
-// alloc before invoking any test.
+// alloc before invoking any test, and add test environments.
int main(int argc, char** argv) {
- FXMEM_InitializePartitionAlloc();
+ FX_InitializeMemoryAllocators();
+
+ // PDF test environment will be deleted by gtest.
+ AddGlobalTestEnvironment(new PDFTestEnvironment());
#ifdef PDF_ENABLE_V8
- std::unique_ptr<v8::Platform> platform;
-#ifdef V8_USE_EXTERNAL_STARTUP_DATA
- static v8::StartupData* snapshot = new v8::StartupData;
- platform =
- InitializeV8ForPDFiumWithStartupData(argv[0], std::string(), snapshot);
-#else // V8_USE_EXTERNAL_STARTUP_DATA
- platform = InitializeV8ForPDFium(argv[0]);
-#endif // V8_USE_EXTERNAL_STARTUP_DATA
-#endif // PDF_ENABLE_V8
-
- InitializePDFTestEnvironment();
+ // V8 test environment will be deleted by gtest.
+ AddGlobalTestEnvironment(new V8TestEnvironment(argv[0]));
#ifdef PDF_ENABLE_XFA
- InitializeXFATestEnvironment();
+ // XFA test environment will be deleted by gtest.
+ AddGlobalTestEnvironment(new XFATestEnvironment());
#endif // PDF_ENABLE_XFA
+#endif // PDF_ENABLE_V8
testing::InitGoogleTest(&argc, argv);
testing::InitGoogleMock(&argc, argv);
- int ret_val = RUN_ALL_TESTS();
-
- DestroyPDFTestEnvironment();
- // NOTE: XFA test environment, if present, destroyed by gtest.
-
-#ifdef PDF_ENABLE_V8
- v8::V8::ShutdownPlatform();
-#endif // PDF_ENABLE_V8
-
- return ret_val;
+ return RUN_ALL_TESTS();
}
diff --git a/testing/utils/bitmap_saver.cpp b/testing/utils/bitmap_saver.cpp
index 524a9f6..40a1339 100644
--- a/testing/utils/bitmap_saver.cpp
+++ b/testing/utils/bitmap_saver.cpp
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -9,7 +9,7 @@
#include "core/fxcrt/fx_safe_types.h"
#include "testing/image_diff/image_diff_png.h"
-#include "third_party/base/logging.h"
+#include "third_party/base/check.h"
// static
void BitmapSaver::WriteBitmapToPng(FPDF_BITMAP bitmap,
@@ -27,8 +27,11 @@
pdfium::base::ValueOrDieForType<size_t>(size));
std::vector<uint8_t> png;
- if (FPDFBitmap_GetFormat(bitmap) == FPDFBitmap_Gray) {
+ int format = FPDFBitmap_GetFormat(bitmap);
+ if (format == FPDFBitmap_Gray) {
png = image_diff_png::EncodeGrayPNG(input, width, height, stride);
+ } else if (format == FPDFBitmap_BGR) {
+ png = image_diff_png::EncodeBGRPNG(input, width, height, stride);
} else {
png = image_diff_png::EncodeBGRAPNG(input, width, height, stride,
/*discard_transparency=*/false);
diff --git a/testing/utils/bitmap_saver.h b/testing/utils/bitmap_saver.h
index 9f931fc..8a9b2c6 100644
--- a/testing/utils/bitmap_saver.h
+++ b/testing/utils/bitmap_saver.h
@@ -1,4 +1,4 @@
-// Copyright 2018 PDFium Authors. All rights reserved.
+// Copyright 2018 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
diff --git a/testing/utils/file_util.cpp b/testing/utils/file_util.cpp
index 5d26d98..6998f0e 100644
--- a/testing/utils/file_util.cpp
+++ b/testing/utils/file_util.cpp
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -8,6 +8,7 @@
#include <string.h>
#include "testing/utils/path_service.h"
+#include "third_party/base/numerics/safe_conversions.h"
std::unique_ptr<char, pdfium::FreeDeleter> GetFileContents(const char* filename,
size_t* retlen) {
@@ -55,7 +56,7 @@
unsigned char* pBuf,
unsigned long size) {
memcpy(pBuf, file_contents_.get() + pos, size);
- return size;
+ return pdfium::base::checked_cast<int>(size);
}
// static
diff --git a/testing/utils/file_util.h b/testing/utils/file_util.h
index 352b882..e91f3d4 100644
--- a/testing/utils/file_util.h
+++ b/testing/utils/file_util.h
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
diff --git a/testing/utils/hash.cpp b/testing/utils/hash.cpp
index 3170de2..5b4a2cf 100644
--- a/testing/utils/hash.cpp
+++ b/testing/utils/hash.cpp
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -18,8 +18,8 @@
return ret;
}
-std::string GenerateMD5Base16(const uint8_t* data, uint32_t size) {
+std::string GenerateMD5Base16(pdfium::span<const uint8_t> data) {
uint8_t digest[16];
- CRYPT_MD5Generate({data, size}, digest);
+ CRYPT_MD5Generate(data, digest);
return CryptToBase16(digest);
}
diff --git a/testing/utils/hash.h b/testing/utils/hash.h
index 0e55165..c2164e8 100644
--- a/testing/utils/hash.h
+++ b/testing/utils/hash.h
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -7,7 +7,9 @@
#include <string>
+#include "third_party/base/span.h"
+
std::string CryptToBase16(const uint8_t* digest);
-std::string GenerateMD5Base16(const uint8_t* data, uint32_t size);
+std::string GenerateMD5Base16(pdfium::span<const uint8_t> data);
#endif // TESTING_UTILS_HASH_H_
diff --git a/testing/utils/path_service.cpp b/testing/utils/path_service.cpp
index 5e1ce39..01a1bfe 100644
--- a/testing/utils/path_service.cpp
+++ b/testing/utils/path_service.cpp
@@ -1,14 +1,19 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "testing/utils/path_service.h"
+#include <stddef.h>
+
#ifdef _WIN32
#include <Windows.h>
#elif defined(__APPLE__)
#include <mach-o/dyld.h>
#include <sys/stat.h>
+#elif defined(__Fuchsia__)
+#include <sys/stat.h>
+#include <unistd.h>
#else // Linux
#include <linux/limits.h>
#include <sys/stat.h>
@@ -18,10 +23,12 @@
#include <string>
#include "core/fxcrt/fx_system.h"
+#include "third_party/base/check.h"
namespace {
-#if defined(__APPLE__) || (defined(ANDROID) && __ANDROID_API__ < 21)
+#if defined(__APPLE__) || defined(__Fuchsia__) || \
+ (defined(ANDROID) && __ANDROID_API__ < 21)
using stat_wrapper_t = struct stat;
int CallStat(const char* path, stat_wrapper_t* sb) {
@@ -68,7 +75,7 @@
return false;
*path = std::string(path_buffer);
#elif defined(__APPLE__)
- ASSERT(path);
+ DCHECK(path);
unsigned int path_length = 0;
_NSGetExecutablePath(NULL, &path_length);
if (path_length == 0)
@@ -89,10 +96,10 @@
#endif // _WIN32
// Get the directory path.
- std::size_t pos = path->size() - 1;
+ size_t pos = path->size() - 1;
if (EndsWithSeparator(*path))
pos--;
- std::size_t found = path->find_last_of(PATH_SEPARATOR, pos);
+ size_t found = path->find_last_of(PATH_SEPARATOR, pos);
if (found == std::string::npos)
return false;
path->resize(found);
@@ -160,3 +167,39 @@
path->append(file_name);
return true;
}
+
+// static
+bool PathService::GetThirdPartyFilePath(const std::string& file_name,
+ std::string* path) {
+ if (!GetSourceDir(path))
+ return false;
+
+ if (!EndsWithSeparator(*path))
+ path->push_back(PATH_SEPARATOR);
+
+ std::string potential_path = *path;
+ potential_path.append("third_party");
+
+ // Use third_party/bigint as a way to distinguish PDFium's vs. other's.
+ std::string bigint = potential_path + PATH_SEPARATOR + "bigint";
+ if (PathService::DirectoryExists(bigint)) {
+ *path = potential_path;
+ path->append(PATH_SEPARATOR + file_name);
+ return true;
+ }
+
+ potential_path = *path;
+ potential_path.append("third_party");
+ potential_path.push_back(PATH_SEPARATOR);
+ potential_path.append("pdfium");
+ potential_path.push_back(PATH_SEPARATOR);
+ potential_path.append("third_party");
+ bigint = potential_path + PATH_SEPARATOR + "bigint";
+ if (PathService::DirectoryExists(potential_path)) {
+ *path = potential_path;
+ path->append(PATH_SEPARATOR + file_name);
+ return true;
+ }
+
+ return false;
+}
diff --git a/testing/utils/path_service.h b/testing/utils/path_service.h
index 63df808..a6582c3 100644
--- a/testing/utils/path_service.h
+++ b/testing/utils/path_service.h
@@ -1,4 +1,4 @@
-// Copyright 2015 PDFium Authors. All rights reserved.
+// Copyright 2015 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -37,5 +37,9 @@
// Get the full path for a test file under the test data directory.
static bool GetTestFilePath(const std::string& file_name, std::string* path);
+
+ // Get the full path for a file under the third-party directory.
+ static bool GetThirdPartyFilePath(const std::string& file_name,
+ std::string* path);
};
#endif // TESTING_UTILS_PATH_SERVICE_H_
diff --git a/testing/v8_initializer.cpp b/testing/v8_initializer.cpp
index 8564b2e..f4ed611 100644
--- a/testing/v8_initializer.cpp
+++ b/testing/v8_initializer.cpp
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -9,8 +9,14 @@
#include "public/fpdfview.h"
#include "testing/utils/file_util.h"
#include "testing/utils/path_service.h"
+#include "third_party/base/numerics/safe_conversions.h"
#include "v8/include/libplatform/libplatform.h"
-#include "v8/include/v8.h"
+#include "v8/include/v8-initialization.h"
+#include "v8/include/v8-snapshot.h"
+
+#ifdef PDF_ENABLE_XFA
+#include "v8/include/cppgc/platform.h"
+#endif
namespace {
@@ -49,20 +55,27 @@
return false;
result_data->data = data_buffer.release();
- result_data->raw_size = data_length;
+ result_data->raw_size = pdfium::base::checked_cast<int>(data_length);
return true;
}
#endif // V8_USE_EXTERNAL_STARTUP_DATA
-std::unique_ptr<v8::Platform> InitializeV8Common(const std::string& exe_path) {
+std::unique_ptr<v8::Platform> InitializeV8Common(const std::string& exe_path,
+ const std::string& js_flags) {
v8::V8::InitializeICUDefaultLocation(exe_path.c_str());
std::unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform();
v8::V8::InitializePlatform(platform.get());
+#ifdef PDF_ENABLE_XFA
+ cppgc::InitializeProcess(platform->GetPageAllocator());
+#endif
const char* recommended_v8_flags = FPDF_GetRecommendedV8Flags();
v8::V8::SetFlagsFromString(recommended_v8_flags);
+ if (!js_flags.empty())
+ v8::V8::SetFlagsFromString(js_flags.c_str());
+
// By enabling predictable mode, V8 won't post any background tasks.
// By enabling GC, it makes it easier to chase use-after-free.
static const char kAdditionalV8Flags[] = "--predictable --expose-gc";
@@ -77,9 +90,11 @@
#ifdef V8_USE_EXTERNAL_STARTUP_DATA
std::unique_ptr<v8::Platform> InitializeV8ForPDFiumWithStartupData(
const std::string& exe_path,
+ const std::string& js_flags,
const std::string& bin_dir,
v8::StartupData* snapshot_blob) {
- std::unique_ptr<v8::Platform> platform = InitializeV8Common(exe_path);
+ std::unique_ptr<v8::Platform> platform =
+ InitializeV8Common(exe_path, js_flags);
if (snapshot_blob) {
if (!GetExternalData(exe_path, bin_dir, "snapshot_blob.bin", snapshot_blob))
return nullptr;
@@ -89,7 +104,16 @@
}
#else // V8_USE_EXTERNAL_STARTUP_DATA
std::unique_ptr<v8::Platform> InitializeV8ForPDFium(
- const std::string& exe_path) {
- return InitializeV8Common(exe_path);
+ const std::string& exe_path,
+ const std::string& js_flags) {
+ return InitializeV8Common(exe_path, js_flags);
}
#endif // V8_USE_EXTERNAL_STARTUP_DATA
+
+void ShutdownV8ForPDFium() {
+#ifdef PDF_ENABLE_XFA
+ cppgc::ShutdownProcess();
+#endif
+ v8::V8::Dispose();
+ v8::V8::DisposePlatform();
+}
diff --git a/testing/v8_initializer.h b/testing/v8_initializer.h
index bd2301c..ecd5911 100644
--- a/testing/v8_initializer.h
+++ b/testing/v8_initializer.h
@@ -1,4 +1,4 @@
-// Copyright 2019 PDFium Authors. All rights reserved.
+// Copyright 2019 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -23,11 +23,13 @@
// |snapshot_blob| is an optional out parameter.
std::unique_ptr<v8::Platform> InitializeV8ForPDFiumWithStartupData(
const std::string& exe_path,
+ const std::string& js_flags,
const std::string& bin_dir,
v8::StartupData* snapshot_blob);
#else
std::unique_ptr<v8::Platform> InitializeV8ForPDFium(
+ const std::string& js_flags,
const std::string& exe_path);
#endif
-
+void ShutdownV8ForPDFium();
#endif // TESTING_V8_INITIALIZER_H_
diff --git a/testing/v8_test_environment.cpp b/testing/v8_test_environment.cpp
new file mode 100644
index 0000000..56eead7
--- /dev/null
+++ b/testing/v8_test_environment.cpp
@@ -0,0 +1,76 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/v8_test_environment.h"
+
+#include <memory>
+#include <string>
+
+#include "core/fxcrt/fx_system.h"
+#include "testing/v8_initializer.h"
+#include "third_party/base/check.h"
+#include "v8/include/libplatform/libplatform.h"
+#include "v8/include/v8-isolate.h"
+#include "v8/include/v8-platform.h"
+#include "v8/include/v8-snapshot.h"
+
+namespace {
+
+V8TestEnvironment* g_environment = nullptr;
+
+} // namespace
+
+V8TestEnvironment::V8TestEnvironment(const char* exe_name)
+ : exe_path_(exe_name),
+ array_buffer_allocator_(std::make_unique<CFX_V8ArrayBufferAllocator>()) {
+ DCHECK(!g_environment);
+ g_environment = this;
+}
+
+V8TestEnvironment::~V8TestEnvironment() {
+ DCHECK(g_environment);
+
+#ifdef V8_USE_EXTERNAL_STARTUP_DATA
+ if (startup_data_)
+ free(const_cast<char*>(startup_data_->data));
+#endif // V8_USE_EXTERNAL_STARTUP_DATA
+
+ g_environment = nullptr;
+}
+
+// static
+V8TestEnvironment* V8TestEnvironment::GetInstance() {
+ return g_environment;
+}
+
+// static
+void V8TestEnvironment::PumpPlatformMessageLoop(v8::Isolate* isolate) {
+ v8::Platform* platform = GetInstance()->platform();
+ while (v8::platform::PumpMessageLoop(platform, isolate))
+ continue;
+}
+
+void V8TestEnvironment::SetUp() {
+#ifdef V8_USE_EXTERNAL_STARTUP_DATA
+ if (startup_data_) {
+ platform_ = InitializeV8ForPDFiumWithStartupData(exe_path_, std::string(),
+ std::string(), nullptr);
+ } else {
+ startup_data_ = std::make_unique<v8::StartupData>();
+ platform_ = InitializeV8ForPDFiumWithStartupData(
+ exe_path_, std::string(), std::string(), startup_data_.get());
+ }
+#else
+ platform_ = InitializeV8ForPDFium(std::string(), exe_path_);
+#endif // V8_USE_EXTERNAL_STARTUP_DATA
+
+ v8::Isolate::CreateParams params;
+ params.array_buffer_allocator = array_buffer_allocator_.get();
+ isolate_.reset(v8::Isolate::New(params));
+}
+
+void V8TestEnvironment::TearDown() {
+ isolate_.reset();
+ ShutdownV8ForPDFium();
+}
diff --git a/testing/v8_test_environment.h b/testing/v8_test_environment.h
new file mode 100644
index 0000000..7a030e4
--- /dev/null
+++ b/testing/v8_test_environment.h
@@ -0,0 +1,51 @@
+// Copyright 2020 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TESTING_V8_TEST_ENVIRONMENT_H_
+#define TESTING_V8_TEST_ENVIRONMENT_H_
+
+#ifndef PDF_ENABLE_V8
+#error "V8 must be enabled"
+#endif // PDF_ENABLE_V8
+
+#include <memory>
+
+#include "fxjs/cfx_v8.h"
+#include "fxjs/cfx_v8_array_buffer_allocator.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace v8 {
+class Isolate;
+class Platform;
+class StartupData;
+} // namespace v8
+
+class TestLoader;
+
+class V8TestEnvironment : public testing::Environment {
+ public:
+ explicit V8TestEnvironment(const char* exe_path);
+ ~V8TestEnvironment() override;
+
+ // Note: GetInstance() does not create one if it does not exist,
+ // so the main program must explicitly add this enviroment.
+ static V8TestEnvironment* GetInstance();
+ static void PumpPlatformMessageLoop(v8::Isolate* pIsolate);
+
+ // testing::Environment:
+ void SetUp() override;
+ void TearDown() override;
+
+ v8::Platform* platform() const { return platform_.get(); }
+ v8::Isolate* isolate() const { return isolate_.get(); }
+
+ private:
+ const char* const exe_path_;
+ std::unique_ptr<v8::StartupData> startup_data_;
+ std::unique_ptr<v8::Platform> platform_;
+ std::unique_ptr<CFX_V8ArrayBufferAllocator> array_buffer_allocator_;
+ std::unique_ptr<v8::Isolate, CFX_V8IsolateDeleter> isolate_;
+};
+
+#endif // TESTING_V8_TEST_ENVIRONMENT_H_
diff --git a/testing/xfa_js_embedder_test.cpp b/testing/xfa_js_embedder_test.cpp
index 2f9cc67..2647157 100644
--- a/testing/xfa_js_embedder_test.cpp
+++ b/testing/xfa_js_embedder_test.cpp
@@ -1,47 +1,37 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "testing/xfa_js_embedder_test.h"
+#include <memory>
#include <string>
#include "fpdfsdk/cpdfsdk_helpers.h"
#include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
+#include "fxjs/fxv8.h"
#include "fxjs/xfa/cfxjse_engine.h"
+#include "fxjs/xfa/cfxjse_isolatetracker.h"
#include "fxjs/xfa/cfxjse_value.h"
#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/ptr_util.h"
-#include "xfa/fxfa/cxfa_ffapp.h"
+#include "third_party/base/check_op.h"
+#include "v8/include/v8-container.h"
+#include "v8/include/v8-local-handle.h"
+#include "v8/include/v8-value.h"
-XFAJSEmbedderTest::XFAJSEmbedderTest()
- : array_buffer_allocator_(
- pdfium::MakeUnique<CFX_V8ArrayBufferAllocator>()) {}
+XFAJSEmbedderTest::XFAJSEmbedderTest() = default;
-XFAJSEmbedderTest::~XFAJSEmbedderTest() {}
+XFAJSEmbedderTest::~XFAJSEmbedderTest() = default;
void XFAJSEmbedderTest::SetUp() {
- v8::Isolate::CreateParams params;
- params.array_buffer_allocator = array_buffer_allocator_.get();
- isolate_ = v8::Isolate::New(params);
- ASSERT_TRUE(isolate_);
-
- EmbedderTest::SetExternalIsolate(isolate_);
- EmbedderTest::SetUp();
-
- CXFA_FFApp::SkipFontLoadForTesting(true);
+ JSEmbedderTest::SetUp();
}
void XFAJSEmbedderTest::TearDown() {
- CXFA_FFApp::SkipFontLoadForTesting(false);
-
- value_.reset();
+ value_.Reset();
script_context_ = nullptr;
- EmbedderTest::TearDown();
-
- isolate_->Dispose();
- isolate_ = nullptr;
+ JSEmbedderTest::TearDown();
}
CXFA_Document* XFAJSEmbedderTest::GetXFADocument() const {
@@ -53,7 +43,15 @@
if (!pContext)
return nullptr;
- return pContext->GetXFADoc()->GetXFADoc();
+ CXFA_FFDoc* pFFDoc = pContext->GetXFADoc();
+ if (!pFFDoc)
+ return nullptr;
+
+ return pFFDoc->GetXFADoc();
+}
+
+v8::Local<v8::Value> XFAJSEmbedderTest::GetValue() const {
+ return v8::Local<v8::Value>::New(isolate(), value_);
}
bool XFAJSEmbedderTest::OpenDocumentWithOptions(
@@ -62,37 +60,54 @@
LinearizeOption linearize_option,
JavaScriptOption javascript_option) {
// JS required for XFA.
- ASSERT(javascript_option == JavaScriptOption::kEnableJavaScript);
+ DCHECK_EQ(javascript_option, JavaScriptOption::kEnableJavaScript);
if (!EmbedderTest::OpenDocumentWithOptions(
filename, password, linearize_option, javascript_option)) {
return false;
}
- script_context_ = GetXFADocument()->GetScriptContext();
+ CXFA_Document* pDoc = GetXFADocument();
+ if (!pDoc)
+ return false;
+
+ script_context_ = pDoc->GetScriptContext();
return true;
}
bool XFAJSEmbedderTest::Execute(ByteStringView input) {
+ CFXJSE_ScopeUtil_IsolateHandleContext scope(
+ script_context_->GetJseContextForTest());
if (ExecuteHelper(input))
return true;
- CFXJSE_Value msg(GetIsolate());
- value_->GetObjectPropertyByIdx(1, &msg);
fprintf(stderr, "FormCalc: %.*s\n", static_cast<int>(input.GetLength()),
input.unterminated_c_str());
- // If the parsing of the input fails, then v8 will not run, so there will be
- // no value here to print.
- if (msg.IsString() && !msg.ToWideString().IsEmpty())
- fprintf(stderr, "JS ERROR: %ls\n", msg.ToWideString().c_str());
+
+ v8::Local<v8::Value> result = GetValue();
+ if (!fxv8::IsArray(result))
+ return false;
+
+ v8::Local<v8::Value> msg = fxv8::ReentrantGetArrayElementHelper(
+ isolate(), result.As<v8::Array>(), 1);
+ if (!fxv8::IsString(msg))
+ return false;
+
+ WideString str = fxv8::ReentrantToWideStringHelper(isolate(), msg);
+ if (!str.IsEmpty())
+ fprintf(stderr, "JS ERROR: %ls\n", str.c_str());
return false;
}
bool XFAJSEmbedderTest::ExecuteSilenceFailure(ByteStringView input) {
+ CFXJSE_ScopeUtil_IsolateHandleContext scope(
+ script_context_->GetJseContextForTest());
return ExecuteHelper(input);
}
bool XFAJSEmbedderTest::ExecuteHelper(ByteStringView input) {
- value_ = pdfium::MakeUnique<CFXJSE_Value>(GetIsolate());
- return script_context_->RunScript(CXFA_Script::Type::Formcalc,
- WideString::FromUTF8(input).AsStringView(),
- value_.get(), GetXFADocument()->GetRoot());
+ auto value = std::make_unique<CFXJSE_Value>();
+ bool ret = script_context_->RunScript(
+ CXFA_Script::Type::Formcalc, WideString::FromUTF8(input).AsStringView(),
+ value.get(), GetXFADocument()->GetRoot());
+ value_.Reset(isolate(), value->GetValue(isolate()));
+ return ret;
}
diff --git a/testing/xfa_js_embedder_test.h b/testing/xfa_js_embedder_test.h
index 412ffd9..9c14867 100644
--- a/testing/xfa_js_embedder_test.h
+++ b/testing/xfa_js_embedder_test.h
@@ -1,22 +1,22 @@
-// Copyright 2017 PDFium Authors. All rights reserved.
+// Copyright 2017 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef TESTING_XFA_JS_EMBEDDER_TEST_H_
#define TESTING_XFA_JS_EMBEDDER_TEST_H_
-#include <memory>
#include <string>
-#include "testing/embedder_test.h"
-#include "xfa/fxfa/parser/cxfa_document.h"
-#include "xfa/fxfa/parser/cxfa_node.h"
+#include "core/fxcrt/string_view_template.h"
+#include "testing/js_embedder_test.h"
+#include "v8/include/v8-local-handle.h"
+#include "v8/include/v8-persistent-handle.h"
+#include "v8/include/v8-value.h"
class CFXJSE_Engine;
-class CFXJSE_Value;
-class CFX_V8ArrayBufferAllocator;
+class CXFA_Document;
-class XFAJSEmbedderTest : public EmbedderTest {
+class XFAJSEmbedderTest : public JSEmbedderTest {
public:
XFAJSEmbedderTest();
~XFAJSEmbedderTest() override;
@@ -29,22 +29,18 @@
LinearizeOption linearize_option,
JavaScriptOption javascript_option) override;
- v8::Isolate* GetIsolate() const { return isolate_; }
CXFA_Document* GetXFADocument() const;
+ CFXJSE_Engine* GetScriptContext() const { return script_context_; }
+ v8::Local<v8::Value> GetValue() const;
bool Execute(ByteStringView input);
bool ExecuteSilenceFailure(ByteStringView input);
- CFXJSE_Engine* GetScriptContext() const { return script_context_; }
- CFXJSE_Value* GetValue() const { return value_.get(); }
-
private:
- std::unique_ptr<CFX_V8ArrayBufferAllocator> array_buffer_allocator_;
- std::unique_ptr<CFXJSE_Value> value_;
- v8::Isolate* isolate_ = nullptr;
- CFXJSE_Engine* script_context_ = nullptr;
-
bool ExecuteHelper(ByteStringView input);
+
+ v8::Global<v8::Value> value_;
+ CFXJSE_Engine* script_context_ = nullptr;
};
#endif // TESTING_XFA_JS_EMBEDDER_TEST_H_
diff --git a/testing/xfa_test_environment.cpp b/testing/xfa_test_environment.cpp
new file mode 100644
index 0000000..07f09b6
--- /dev/null
+++ b/testing/xfa_test_environment.cpp
@@ -0,0 +1,40 @@
+// Copyright 2019 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/xfa_test_environment.h"
+
+#include "core/fxge/cfx_fontmgr.h"
+#include "core/fxge/cfx_gemodule.h"
+#include "core/fxge/systemfontinfo_iface.h"
+#include "third_party/base/check.h"
+#include "xfa/fgas/font/cfgas_gemodule.h"
+
+namespace {
+
+XFATestEnvironment* g_env = nullptr;
+
+} // namespace
+
+XFATestEnvironment::XFATestEnvironment() {
+ DCHECK(!g_env);
+ g_env = this;
+}
+
+XFATestEnvironment::~XFATestEnvironment() {
+ DCHECK(g_env);
+ g_env = nullptr;
+}
+
+void XFATestEnvironment::SetUp() {
+ CFX_GEModule::Get()->GetFontMgr()->GetBuiltinMapper()->SetSystemFontInfo(
+ CFX_GEModule::Get()->GetPlatform()->CreateDefaultSystemFontInfo());
+
+ // The font loading that takes place in CFGAS_GEModule::Create() is slow,
+ // but we do it only once per binary execution, not once per test.
+ CFGAS_GEModule::Create();
+}
+
+void XFATestEnvironment::TearDown() {
+ CFGAS_GEModule::Destroy();
+}
diff --git a/testing/xfa_test_environment.h b/testing/xfa_test_environment.h
new file mode 100644
index 0000000..f8356bd
--- /dev/null
+++ b/testing/xfa_test_environment.h
@@ -0,0 +1,24 @@
+// Copyright 2019 The PDFium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TESTING_XFA_TEST_ENVIRONMENT_H_
+#define TESTING_XFA_TEST_ENVIRONMENT_H_
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+#ifndef PDF_ENABLE_XFA
+#error "XFA must be enabled"
+#endif
+
+class XFATestEnvironment : public testing::Environment {
+ public:
+ XFATestEnvironment();
+ ~XFATestEnvironment();
+
+ // testing::TestEnvironment:
+ void SetUp() override;
+ void TearDown() override;
+};
+
+#endif // TESTING_XFA_TEST_ENVIRONMENT_H_
diff --git a/testing/xfa_unit_test_support.cpp b/testing/xfa_unit_test_support.cpp
deleted file mode 100644
index 8cc2d6c..0000000
--- a/testing/xfa_unit_test_support.cpp
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright 2019 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 "testing/xfa_unit_test_support.h"
-
-#include <memory>
-#include <utility>
-
-#include "core/fxge/cfx_fontmgr.h"
-#include "core/fxge/cfx_gemodule.h"
-#include "core/fxge/systemfontinfo_iface.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/base/ptr_util.h"
-#include "xfa/fgas/font/cfgas_fontmgr.h"
-
-namespace {
-
-// The loading time of the CFGAS_FontMgr is linear in the number of times it is
-// loaded. So, if a test suite has a lot of tests that need a font manager they
-// can end up executing very, very slowly.
-class XFATestEnvironment final : public testing::Environment {
- public:
- // testing::Environment:
- void SetUp() override {
- auto font_mgr = pdfium::MakeUnique<CFGAS_FontMgr>();
- if (font_mgr->EnumFonts())
- font_mgr_ = std::move(font_mgr);
- }
- void TearDown() override { font_mgr_.reset(); }
-
- CFGAS_FontMgr* FontManager() const { return font_mgr_.get(); }
-
- private:
- std::unique_ptr<CFGAS_FontMgr> font_mgr_;
-};
-
-XFATestEnvironment* g_env = nullptr;
-
-} // namespace
-
-void InitializeXFATestEnvironment() {
- // |g_env| will be deleted by gtest.
- g_env = new XFATestEnvironment();
- AddGlobalTestEnvironment(g_env);
-
- // TODO(dsinclair): This font loading is slow. We should make a test font
- // loader which loads up a single font we use in all tests.
- CFX_GEModule::Get()->GetFontMgr()->SetSystemFontInfo(
- SystemFontInfoIface::CreateDefault(nullptr));
-}
-
-CFGAS_FontMgr* GetGlobalFontManager() {
- return g_env->FontManager();
-}
diff --git a/testing/xfa_unit_test_support.h b/testing/xfa_unit_test_support.h
deleted file mode 100644
index 0a54778..0000000
--- a/testing/xfa_unit_test_support.h
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2019 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.
-
-#ifndef TESTING_XFA_UNIT_TEST_SUPPORT_H_
-#define TESTING_XFA_UNIT_TEST_SUPPORT_H_
-
-#ifndef PDF_ENABLE_XFA
-#error "XFA must be enabled"
-#endif
-
-class CFGAS_FontMgr;
-
-void InitializeXFATestEnvironment();
-
-CFGAS_FontMgr* GetGlobalFontManager();
-
-#endif // TESTING_XFA_UNIT_TEST_SUPPORT_H_