From c853ffb5b2f75f5a889ed2e3ef89b818a736e87a Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 13 Apr 2024 13:50:49 +0200 Subject: Adding upstream version 1.3+ds. Signed-off-by: Daniel Baumann --- testfiles/CMakeLists.txt | 137 + testfiles/CTestCustom.cmake.in | 5 + testfiles/cli_tests/CMakeLists.txt | 1006 + testfiles/cli_tests/check_output.sh | 86 + testfiles/cli_tests/compare.sh | 13 + testfiles/cli_tests/identify.sh | 11 + testfiles/cli_tests/l2compare.sh | 91 + testfiles/cli_tests/match_regex.sh | 13 + testfiles/cli_tests/match_regex_fail.sh | 13 + testfiles/cli_tests/test_no_output_file.sh | 10 + .../testcases/actions-file-open_expected.png | Bin 0 -> 1502 bytes .../testcases/actions-object-align_expected.png | Bin 0 -> 417 bytes .../actions-object-distribute_expected.png | Bin 0 -> 492 bytes .../actions-object-set-attribute_expected.png | Bin 0 -> 1082 bytes .../actions-object-simplify-path_expected.png | Bin 0 -> 12781 bytes .../actions-object-stroke-to-path_expected.png | Bin 0 -> 723 bytes .../actions-object-unlink-clones_expected.png | Bin 0 -> 1269 bytes .../testcases/actions-open-page_expected.png | Bin 0 -> 21763 bytes .../testcases/actions-transform-grow_expected.png | Bin 0 -> 1086 bytes .../actions-transform-remove_expected.png | Bin 0 -> 1131 bytes .../actions-transform-rotate_expected.png | Bin 0 -> 1969 bytes .../actions-transform-translate_expected.png | Bin 0 -> 1086 bytes testfiles/cli_tests/testcases/areas.svg | 10 + testfiles/cli_tests/testcases/empty.svg | 3 + .../testcases/export-area-drawing_expected.emf | Bin 0 -> 1856 bytes .../testcases/export-area-drawing_expected.eps | 443 + .../testcases/export-area-drawing_expected.pdf | Bin 0 -> 1338 bytes .../testcases/export-area-drawing_expected.png | Bin 0 -> 13995 bytes .../testcases/export-area-drawing_expected.ps | 480 + .../testcases/export-area-drawing_expected.svg | 65 + .../testcases/export-area-drawing_expected.wmf | Bin 0 -> 1006 bytes .../testcases/export-area-page_expected.emf | Bin 0 -> 1856 bytes .../testcases/export-area-page_expected.eps | 443 + .../testcases/export-area-page_expected.pdf | Bin 0 -> 1350 bytes .../testcases/export-area-page_expected.png | Bin 0 -> 15117 bytes .../testcases/export-area-page_expected.ps | 482 + .../testcases/export-area-page_expected.svg | 61 + .../testcases/export-area-page_expected.wmf | Bin 0 -> 1006 bytes .../testcases/export-area-page_export-id.pdf | 69 + .../testcases/export-area-page_export-id.png | Bin 0 -> 10167 bytes .../testcases/export-area-page_export-id.ps | 130 + .../testcases/export-area-page_export-id.svg | 52 + .../testcases/export-area-snap_expected.png | Bin 0 -> 1048 bytes .../cli_tests/testcases/export-area_expected.png | Bin 0 -> 5778 bytes .../export-background-opacity_expected.eps | 86 + .../export-background-opacity_expected.pdf | Bin 0 -> 1174 bytes .../export-background-opacity_expected.png | Bin 0 -> 428 bytes .../export-background-opacity_expected.ps | 123 + .../export-background-opacity_expected.svg | 54 + .../testcases/export-background_expected.emf | Bin 0 -> 1144 bytes .../testcases/export-background_expected.eps | 86 + .../testcases/export-background_expected.pdf | Bin 0 -> 1140 bytes .../testcases/export-background_expected.png | Bin 0 -> 421 bytes .../testcases/export-background_expected.ps | 123 + .../testcases/export-background_expected.svg | 52 + .../testcases/export-background_expected.wmf | Bin 0 -> 488 bytes .../testcases/export-clone-group-filtered-mp.svg | 101 + .../export-clone-group-filtered-mp_expected.png | Bin 0 -> 1522 bytes .../export-default-background_expected.eps | 86 + .../export-default-background_expected.pdf | 70 + .../export-default-background_expected.png | Bin 0 -> 435 bytes .../export-default-background_expected.ps | 123 + .../export-default-background_expected.svg | 54 + .../cli_tests/testcases/export-dpi_expected.eps | 282 + .../cli_tests/testcases/export-dpi_expected.pdf | Bin 0 -> 2193 bytes .../cli_tests/testcases/export-dpi_expected.png | Bin 0 -> 763 bytes .../cli_tests/testcases/export-dpi_expected.ps | 319 + .../testcases/export-filtered-clones-mp.svg | 285 + .../export-filtered-clones-mp_expected.png | Bin 0 -> 5034 bytes .../cli_tests/testcases/export-grouped-mp.svg | 95 + .../testcases/export-grouped-mp_expected.png | Bin 0 -> 1619 bytes .../cli_tests/testcases/export-height_expected.png | Bin 0 -> 248 bytes .../cli_tests/testcases/export-id_expected.emf | Bin 0 -> 788 bytes .../cli_tests/testcases/export-id_expected.eps | 82 + .../cli_tests/testcases/export-id_expected.pdf | Bin 0 -> 940 bytes .../cli_tests/testcases/export-id_expected.png | Bin 0 -> 388 bytes .../cli_tests/testcases/export-id_expected.ps | 119 + .../cli_tests/testcases/export-id_expected.svg | 110 + .../cli_tests/testcases/export-id_expected.wmf | Bin 0 -> 884 bytes .../export-id_export-id-only_expected.emf | Bin 0 -> 664 bytes .../export-id_export-id-only_expected.eps | 80 + .../export-id_export-id-only_expected.pdf | Bin 0 -> 1099 bytes .../export-id_export-id-only_expected.png | Bin 0 -> 371 bytes .../testcases/export-id_export-id-only_expected.ps | 117 + .../export-id_export-id-only_expected.svg | 47 + .../export-id_export-id-only_expected.wmf | Bin 0 -> 290 bytes ...export-id-only_export-area-drawing_expected.emf | Bin 0 -> 704 bytes ...export-id-only_export-area-drawing_expected.eps | 80 + ...export-id-only_export-area-drawing_expected.pdf | Bin 0 -> 932 bytes ...export-id-only_export-area-drawing_expected.png | Bin 0 -> 730 bytes ..._export-id-only_export-area-drawing_expected.ps | 117 + ...export-id-only_export-area-drawing_expected.svg | 47 + ...export-id-only_export-area-drawing_expected.wmf | Bin 0 -> 290 bytes ...id_export-id-only_export-area-page_expected.emf | Bin 0 -> 700 bytes ...id_export-id-only_export-area-page_expected.eps | 80 + ...id_export-id-only_export-area-page_expected.pdf | Bin 0 -> 1109 bytes ...id_export-id-only_export-area-page_expected.png | Bin 0 -> 979 bytes ...-id_export-id-only_export-area-page_expected.ps | 117 + ...id_export-id-only_export-area-page_expected.svg | 47 + ...id_export-id-only_export-area-page_expected.wmf | Bin 0 -> 290 bytes .../testcases/export-ignore-filters_expected.emf | Bin 0 -> 816 bytes .../testcases/export-ignore-filters_expected.eps | 82 + .../testcases/export-ignore-filters_expected.pdf | Bin 0 -> 1105 bytes .../testcases/export-ignore-filters_expected.png | Bin 0 -> 402 bytes .../testcases/export-ignore-filters_expected.ps | 119 + .../testcases/export-ignore-filters_expected.svg | 54 + .../testcases/export-ignore-filters_expected.wmf | Bin 0 -> 356 bytes .../testcases/export-margin_drawing_expected.emf | Bin 0 -> 812 bytes .../testcases/export-margin_drawing_expected.eps | 82 + .../testcases/export-margin_drawing_expected.pdf | Bin 0 -> 940 bytes .../testcases/export-margin_drawing_expected.png | Bin 0 -> 508 bytes .../testcases/export-margin_drawing_expected.ps | 119 + .../testcases/export-margin_drawing_expected.svg | 58 + .../testcases/export-margin_drawing_expected.wmf | Bin 0 -> 356 bytes .../export-margin_export-area_expected.png | Bin 0 -> 356 bytes .../testcases/export-margin_export-id_expected.emf | Bin 0 -> 832 bytes .../testcases/export-margin_export-id_expected.eps | 82 + .../testcases/export-margin_export-id_expected.pdf | Bin 0 -> 943 bytes .../testcases/export-margin_export-id_expected.png | Bin 0 -> 485 bytes .../testcases/export-margin_export-id_expected.ps | 119 + .../testcases/export-margin_export-id_expected.svg | 58 + .../testcases/export-margin_export-id_expected.wmf | Bin 0 -> 356 bytes ...rt-margin_export-id_export-id-only_expected.emf | Bin 0 -> 692 bytes ...rt-margin_export-id_export-id-only_expected.eps | 80 + ...rt-margin_export-id_export-id-only_expected.pdf | Bin 0 -> 933 bytes ...rt-margin_export-id_export-id-only_expected.png | Bin 0 -> 482 bytes ...ort-margin_export-id_export-id-only_expected.ps | 117 + ...rt-margin_export-id_export-id-only_expected.svg | 51 + ...rt-margin_export-id_export-id-only_expected.wmf | Bin 0 -> 290 bytes .../testcases/export-margin_mm_expected.emf | Bin 0 -> 824 bytes .../testcases/export-margin_mm_expected.eps | 82 + .../testcases/export-margin_mm_expected.pdf | Bin 0 -> 1152 bytes .../testcases/export-margin_mm_expected.png | Bin 0 -> 6068 bytes .../testcases/export-margin_mm_expected.ps | 119 + .../testcases/export-margin_mm_expected.svg | 58 + .../testcases/export-margin_mm_expected.wmf | Bin 0 -> 356 bytes .../export-margin_mm_viewbox_drawing_expected.emf | Bin 0 -> 856 bytes .../export-margin_mm_viewbox_drawing_expected.eps | 82 + .../export-margin_mm_viewbox_drawing_expected.pdf | Bin 0 -> 974 bytes .../export-margin_mm_viewbox_drawing_expected.png | Bin 0 -> 3222 bytes .../export-margin_mm_viewbox_drawing_expected.ps | 119 + .../export-margin_mm_viewbox_drawing_expected.svg | 64 + .../export-margin_mm_viewbox_drawing_expected.wmf | Bin 0 -> 356 bytes .../export-margin_mm_viewbox_id_expected.emf | Bin 0 -> 844 bytes .../export-margin_mm_viewbox_id_expected.eps | 82 + .../export-margin_mm_viewbox_id_expected.pdf | Bin 0 -> 972 bytes .../export-margin_mm_viewbox_id_expected.png | Bin 0 -> 2664 bytes .../export-margin_mm_viewbox_id_expected.ps | 119 + .../export-margin_mm_viewbox_id_expected.svg | 50 + .../export-margin_mm_viewbox_id_expected.wmf | Bin 0 -> 356 bytes .../export-margin_mm_viewbox_page_expected.emf | Bin 0 -> 824 bytes .../export-margin_mm_viewbox_page_expected.eps | 82 + .../export-margin_mm_viewbox_page_expected.pdf | Bin 0 -> 1152 bytes .../export-margin_mm_viewbox_page_expected.png | Bin 0 -> 6068 bytes .../export-margin_mm_viewbox_page_expected.ps | 119 + .../export-margin_mm_viewbox_page_expected.svg | 64 + .../export-margin_mm_viewbox_page_expected.wmf | Bin 0 -> 356 bytes .../testcases/export-margin_px_expected.emf | Bin 0 -> 820 bytes .../testcases/export-margin_px_expected.eps | 82 + .../testcases/export-margin_px_expected.pdf | Bin 0 -> 943 bytes .../testcases/export-margin_px_expected.png | Bin 0 -> 737 bytes .../testcases/export-margin_px_expected.ps | 119 + .../testcases/export-margin_px_expected.svg | 58 + .../testcases/export-margin_px_expected.wmf | Bin 0 -> 356 bytes .../cli_tests/testcases/export-marker-on-clone.svg | 106 + .../testcases/export-marker-on-clone_expected.png | Bin 0 -> 1993 bytes .../export-png-color-mode-gray-8_expected.png | Bin 0 -> 6386 bytes .../export-png-color-mode-rgb-8_expected.png | Bin 0 -> 11485 bytes .../export-png-color-mode-rgba-8_expected.png | Bin 0 -> 15278 bytes .../testcases/export-ps-level-2_expected.ps | 2366 ++ .../testcases/export-ps-level-3_expected.ps | 134 + .../cli_tests/testcases/export-text-paintorder.svg | 31 + .../testcases/export-text-paintorder_expected.png | Bin 0 -> 7277 bytes .../cli_tests/testcases/export-width_expected.png | Bin 0 -> 644 bytes .../testcases/export-with-filters-multipage.svg | 58 + .../export-with-filters-multipage_expected.png | Bin 0 -> 21441 bytes .../testcases/export-with-filters_expected.emf | Bin 0 -> 800 bytes .../testcases/export-with-filters_expected.eps | 122 + .../testcases/export-with-filters_expected.pdf | Bin 0 -> 2206 bytes .../testcases/export-with-filters_expected.png | Bin 0 -> 402 bytes .../testcases/export-with-filters_expected.ps | 159 + .../testcases/export-with-filters_expected.svg | 68 + .../testcases/export-with-filters_expected.wmf | Bin 0 -> 356 bytes testfiles/cli_tests/testcases/export_hints.svg | 7 + testfiles/cli_tests/testcases/filter.svg | 9 + testfiles/cli_tests/testcases/gradient.svg | 10 + .../cli_tests/testcases/lambda-background.svg | 7 + testfiles/cli_tests/testcases/lambda.svg | 6 + .../testcases/librevenge_formats/corel_draw.cdr | Bin 0 -> 13873 bytes .../testcases/librevenge_formats/corel_draw2.cdr | Bin 0 -> 19596 bytes .../librevenge_formats/corel_draw2_expected.png | Bin 0 -> 30971 bytes .../librevenge_formats/corel_draw_expected.png | Bin 0 -> 15577 bytes .../testcases/librevenge_formats/visio.vsd | Bin 0 -> 52224 bytes .../librevenge_formats/visio.vsd_expected.png | Bin 0 -> 30320 bytes .../testcases/librevenge_formats/visio.vsdx | Bin 0 -> 24693 bytes .../librevenge_formats/visio.vsdx_expected.png | Bin 0 -> 6217 bytes .../testcases/librevenge_formats/word_perfect.wpg | Bin 0 -> 2525 bytes .../librevenge_formats/word_perfect_expected.png | Bin 0 -> 958 bytes .../cli_tests/testcases/multiline-anchoring.svg | 29 + testfiles/cli_tests/testcases/offset.svg | 10 + testfiles/cli_tests/testcases/path.svg | 39 + testfiles/cli_tests/testcases/pdf-mesh.pdf | Bin 0 -> 1728 bytes testfiles/cli_tests/testcases/pdf-pages.pdf | Bin 0 -> 15382 bytes testfiles/cli_tests/testcases/pdfinput/cms.pdf | Bin 0 -> 7514 bytes .../cli_tests/testcases/pdfinput/cms_expected.svg | 53 + .../cli_tests/testcases/pdfinput/font-spacing.pdf | Bin 0 -> 49180 bytes .../testcases/pdfinput/font-spacing_expected.svg | 189 + .../cli_tests/testcases/pdfinput/font-style.pdf | Bin 0 -> 43062 bytes .../testcases/pdfinput/font-style_expected.svg | 99 + .../cli_tests/testcases/pdfinput/gradients.pdf | Bin 0 -> 7305 bytes .../testcases/pdfinput/gradients_expected.svg | 188 + testfiles/cli_tests/testcases/pdfinput/latex.pdf | Bin 0 -> 67919 bytes .../testcases/pdfinput/latex_expected.svg | 122 + .../testcases/pdfinput/multi-page-sample.pdf | Bin 0 -> 702121 bytes .../pdfinput/multi-page-sample_expected.svg | 1606 ++ .../testcases/pdfinput/rotated-gradients.pdf | Bin 0 -> 3420 bytes .../pdfinput/rotated-gradients_expected.svg | 95 + .../testcases/pdfinput/transparency-groups.pdf | Bin 0 -> 3558 bytes .../pdfinput/transparency-groups_expected.svg | 142 + testfiles/cli_tests/testcases/pyramids.svg | 22 + testfiles/cli_tests/testcases/rects.svg | 6 + testfiles/cli_tests/testcases/regression-1364.svg | 20 + .../cli_tests/testcases/regression-1364_script.py | 31 + testfiles/cli_tests/testcases/regression-2602.svg | 9 + .../cli_tests/testcases/regression-2602_script.py | 10 + testfiles/cli_tests/testcases/regression-2797.svg | 22 + .../cli_tests/testcases/regression-2797_script.py | 17 + testfiles/cli_tests/testcases/shapes.svg | 8 + testfiles/cli_tests/testcases/shapes_expected.emf | Bin 0 -> 1540 bytes testfiles/cli_tests/testcases/shapes_expected.eps | 111 + testfiles/cli_tests/testcases/shapes_expected.pdf | Bin 0 -> 1333 bytes testfiles/cli_tests/testcases/shapes_expected.png | Bin 0 -> 13382 bytes testfiles/cli_tests/testcases/shapes_expected.ps | 146 + testfiles/cli_tests/testcases/shapes_expected.wmf | Bin 0 -> 1120 bytes testfiles/cli_tests/testcases/shapes_expected.xaml | 2 + testfiles/cli_tests/testcases/shapes_pre_0.92.svg | 9 + .../testcases/shapes_pre_0.92_scaled_expected.png | Bin 0 -> 15415 bytes testfiles/cli_tests/testcases/square_mm.svg | 5 + .../cli_tests/testcases/square_mm_viewbox.svg | 10 + testfiles/cli_tests/testcases/square_px.svg | 5 + .../testcases/stroke-to-path-variations.svg | 131 + testfiles/cli_tests/testcases/systemLanguage.svg | 20 + .../cli_tests/testcases/systemLanguage_RDF.svg | 15 + .../cli_tests/testcases/systemLanguage_de.png | Bin 0 -> 516 bytes .../cli_tests/testcases/systemLanguage_default.png | Bin 0 -> 516 bytes .../cli_tests/testcases/systemLanguage_en.png | Bin 0 -> 519 bytes .../cli_tests/testcases/systemLanguage_fr.png | Bin 0 -> 518 bytes .../cli_tests/testcases/systemLanguage_pt.png | Bin 0 -> 516 bytes testfiles/cli_tests/testcases/text.svg | 4 + testfiles/cli_tests/testcases/text_in_group.svg | 11 + testfiles/cli_tests/testcases/theta.svg | 14 + testfiles/data/crystalegg.xml | 767 + testfiles/data/doc2html.xsl | 64 + testfiles/data/example-FEXTRA-FCOMMENT.gz | Bin 0 -> 92 bytes testfiles/data/visual-bounds.svg | 665 + testfiles/doc-per-case-test.cpp | 50 + testfiles/doc-per-case-test.h | 44 + testfiles/fuzzer.cpp | 22 + testfiles/fuzzer.dict | 526 + testfiles/lpe_tests/CMakeLists.txt | 3 + testfiles/lpe_tests/Inkscape_0_92.svg | 1729 ++ testfiles/lpe_tests/Inkscape_0_92_64.svg | 516 + testfiles/lpe_tests/Inkscape_1_0.svg | 6137 +++++ testfiles/lpe_tests/Inkscape_1_0_64.svg | 2966 +++ testfiles/lpe_tests/Inkscape_1_1.svg | 18291 +++++++++++++++ testfiles/lpe_tests/Inkscape_1_2.svg | 22065 ++++++++++++++++++ testfiles/lpe_tests/Inkscape_1_3.svg | 22404 +++++++++++++++++++ testfiles/lpe_tests/README | 12 + testfiles/lpespaths-test.h | 235 + testfiles/rendering_tests/CMakeLists.txt | 70 + testfiles/rendering_tests/README | 26 + testfiles/rendering_tests/drawing-pattern-test.svg | 74 + .../expected_rendering/multi-style.png | Bin 0 -> 1433 bytes .../selector-important-002-large.png | Bin 0 -> 11306 bytes .../expected_rendering/selector-important-002.png | Bin 0 -> 921 bytes .../selector-important-003-large.png | Bin 0 -> 11308 bytes .../expected_rendering/selector-important-003.png | Bin 0 -> 925 bytes .../expected_rendering/style-parsing.png | Bin 0 -> 765 bytes .../symbol-svg2-geometry-properties.png | Bin 0 -> 1073 bytes .../expected_rendering/symbol-svg2-ref.png | Bin 0 -> 294 bytes .../test-baseline-shift-large.png | Bin 0 -> 97340 bytes .../expected_rendering/test-baseline-shift.png | Bin 0 -> 20024 bytes .../expected_rendering/test-dont-crash.png | Bin 0 -> 453 bytes .../expected_rendering/test-empty-large.png | Bin 0 -> 69082 bytes .../expected_rendering/test-empty.png | Bin 0 -> 6920 bytes .../expected_rendering/test-glyph-y-pos-large.png | Bin 0 -> 115278 bytes .../expected_rendering/test-glyph-y-pos.png | Bin 0 -> 22881 bytes .../test-powerstroke-join-large.png | Bin 0 -> 8037 bytes .../expected_rendering/test-powerstroke-join.png | Bin 0 -> 1796 bytes .../expected_rendering/test-rtl-vertical-large.png | Bin 0 -> 87221 bytes .../expected_rendering/test-rtl-vertical.png | Bin 0 -> 17482 bytes .../expected_rendering/test-use-large.png | Bin 0 -> 6452 bytes .../expected_rendering/test-use.png | Bin 0 -> 1204 bytes .../expected_rendering/text-flow.png | Bin 0 -> 837687 bytes .../text-glyphs-combining-large.png | Bin 0 -> 172004 bytes .../expected_rendering/text-glyphs-combining.png | Bin 0 -> 35443 bytes .../text-glyphs-vertical-large.png | Bin 0 -> 106286 bytes .../expected_rendering/text-glyphs-vertical.png | Bin 0 -> 22834 bytes .../expected_rendering/text-gzipped-svg-glyph.png | Bin 0 -> 646 bytes .../expected_rendering/text-shaping-large.png | Bin 0 -> 83064 bytes .../expected_rendering/text-shaping.png | Bin 0 -> 33675 bytes testfiles/rendering_tests/fonts/Estedad-Medium.ttf | Bin 0 -> 60832 bytes .../rendering_tests/fonts/GeomTest-Regular.otf | Bin 0 -> 2984 bytes .../fonts/GeomTest-gzipped-SVG-glyphs.otf | Bin 0 -> 3424 bytes testfiles/rendering_tests/fonts/LICENSES | 10 + testfiles/rendering_tests/fonts/Lohit-Telugu.ttf | Bin 0 -> 341752 bytes .../rendering_tests/fonts/NotoSans-Regular.ttf | Bin 0 -> 468584 bytes .../fonts/NotoSansCJKjp-Regular.otf | Bin 0 -> 16427228 bytes .../fonts/NotoSansHebrew-Regular.ttf | Bin 0 -> 27696 bytes testfiles/rendering_tests/multi-style-import-1.css | 9 + testfiles/rendering_tests/multi-style-import-2.css | 7 + testfiles/rendering_tests/multi-style.svg | 94 + .../rendering_tests/selector-important-002.svg | 58 + .../rendering_tests/selector-important-003.svg | 57 + testfiles/rendering_tests/style-parsing.svg | 16 + .../symbol-svg2-geometry-properties.svg | 83 + testfiles/rendering_tests/symbol-svg2-ref.svg | 62 + testfiles/rendering_tests/test-baseline-shift.svg | 33 + testfiles/rendering_tests/test-dont-crash.svg | 51 + testfiles/rendering_tests/test-empty.svg | 65 + testfiles/rendering_tests/test-glyph-y-pos.svg | 32 + .../rendering_tests/test-powerstroke-join.svg | 6 + testfiles/rendering_tests/test-rtl-vertical.svg | 39 + testfiles/rendering_tests/test-use-ref.svg | 89 + testfiles/rendering_tests/test-use.svg | 18 + testfiles/rendering_tests/test.sh | 66 + testfiles/rendering_tests/text-flow.svg | 55 + .../rendering_tests/text-glyphs-combining.svg | 36 + testfiles/rendering_tests/text-glyphs-vertical.svg | 53 + .../rendering_tests/text-gzipped-svg-glyph.svg | 25 + testfiles/rendering_tests/text-shaping.svg | 93 + testfiles/src/2geom-characterization-test.cpp | 31 + testfiles/src/async_channel-test.cpp | 76 + testfiles/src/async_funclog-test.cpp | 120 + testfiles/src/async_progress-test.cpp | 131 + testfiles/src/attributes-test.cpp | 685 + testfiles/src/cairo-utils-test.cpp | 51 + testfiles/src/color-profile-test.cpp | 127 + testfiles/src/curve-test.cpp | 277 + testfiles/src/cxxtests-to-migrate/marker-test.h | 42 + testfiles/src/cxxtests-to-migrate/mod360-test.h | 65 + .../src/cxxtests-to-migrate/preferences-test.h | 139 + .../src/cxxtests-to-migrate/sp-style-elem-test.h | 166 + testfiles/src/cxxtests-to-migrate/test-helpers.h | 71 + testfiles/src/cxxtests-to-migrate/verbs-test.h | 95 + testfiles/src/dir-util-test.cpp | 64 + testfiles/src/drag-and-drop-svgz.cpp | 70 + testfiles/src/drawing-pattern-test.cpp | 117 + testfiles/src/extract-uri-test.cpp | 75 + testfiles/src/lpe-test.cpp | 75 + testfiles/src/lpe64-test.cpp | 30 + testfiles/src/min-bbox-test.cpp | 73 + testfiles/src/object-set-test.cpp | 706 + testfiles/src/object-style-test.cpp | 199 + testfiles/src/object-test.cpp | 204 + testfiles/src/oklab-color-test.cpp | 171 + testfiles/src/path-boolop-test.cpp | 86 + testfiles/src/path-reverse-lpe-test.cpp | 49 + testfiles/src/rebase-hrefs-test.cpp | 135 + testfiles/src/sp-glyph-kerning-test.cpp | 26 + testfiles/src/sp-gradient-test.cpp | 130 + testfiles/src/sp-item-group-test.cpp | 46 + testfiles/src/sp-object-tags-test.cpp | 235 + testfiles/src/sp-object-test.cpp | 122 + testfiles/src/stream-test.cpp | 161 + testfiles/src/style-elem-test.cpp | 71 + testfiles/src/style-internal-test.cpp | 82 + testfiles/src/style-test.cpp | 604 + testfiles/src/svg-affine-test.cpp | 226 + testfiles/src/svg-box-test.cpp | 122 + testfiles/src/svg-color-test.cpp | 112 + testfiles/src/svg-extension-test.cpp | 131 + testfiles/src/svg-length-test.cpp | 225 + testfiles/src/svg-path-geom-test.cpp | 507 + testfiles/src/svg-stringstream-test.cpp | 156 + testfiles/src/uri-test.cpp | 304 + testfiles/src/util-test.cpp | 115 + testfiles/src/visual-bounds-test.cpp | 132 + testfiles/src/xml-test.cpp | 84 + testfiles/unittest.cpp | 51 + testfiles/utils/functions.sh | 77 + 381 files changed, 100780 insertions(+) create mode 100644 testfiles/CMakeLists.txt create mode 100644 testfiles/CTestCustom.cmake.in create mode 100644 testfiles/cli_tests/CMakeLists.txt create mode 100644 testfiles/cli_tests/check_output.sh create mode 100644 testfiles/cli_tests/compare.sh create mode 100644 testfiles/cli_tests/identify.sh create mode 100755 testfiles/cli_tests/l2compare.sh create mode 100644 testfiles/cli_tests/match_regex.sh create mode 100644 testfiles/cli_tests/match_regex_fail.sh create mode 100644 testfiles/cli_tests/test_no_output_file.sh create mode 100644 testfiles/cli_tests/testcases/actions-file-open_expected.png create mode 100644 testfiles/cli_tests/testcases/actions-object-align_expected.png create mode 100644 testfiles/cli_tests/testcases/actions-object-distribute_expected.png create mode 100644 testfiles/cli_tests/testcases/actions-object-set-attribute_expected.png create mode 100644 testfiles/cli_tests/testcases/actions-object-simplify-path_expected.png create mode 100644 testfiles/cli_tests/testcases/actions-object-stroke-to-path_expected.png create mode 100644 testfiles/cli_tests/testcases/actions-object-unlink-clones_expected.png create mode 100644 testfiles/cli_tests/testcases/actions-open-page_expected.png create mode 100644 testfiles/cli_tests/testcases/actions-transform-grow_expected.png create mode 100644 testfiles/cli_tests/testcases/actions-transform-remove_expected.png create mode 100644 testfiles/cli_tests/testcases/actions-transform-rotate_expected.png create mode 100644 testfiles/cli_tests/testcases/actions-transform-translate_expected.png create mode 100644 testfiles/cli_tests/testcases/areas.svg create mode 100644 testfiles/cli_tests/testcases/empty.svg create mode 100644 testfiles/cli_tests/testcases/export-area-drawing_expected.emf create mode 100644 testfiles/cli_tests/testcases/export-area-drawing_expected.eps create mode 100644 testfiles/cli_tests/testcases/export-area-drawing_expected.pdf create mode 100644 testfiles/cli_tests/testcases/export-area-drawing_expected.png create mode 100644 testfiles/cli_tests/testcases/export-area-drawing_expected.ps create mode 100644 testfiles/cli_tests/testcases/export-area-drawing_expected.svg create mode 100644 testfiles/cli_tests/testcases/export-area-drawing_expected.wmf create mode 100644 testfiles/cli_tests/testcases/export-area-page_expected.emf create mode 100644 testfiles/cli_tests/testcases/export-area-page_expected.eps create mode 100644 testfiles/cli_tests/testcases/export-area-page_expected.pdf create mode 100644 testfiles/cli_tests/testcases/export-area-page_expected.png create mode 100644 testfiles/cli_tests/testcases/export-area-page_expected.ps create mode 100644 testfiles/cli_tests/testcases/export-area-page_expected.svg create mode 100644 testfiles/cli_tests/testcases/export-area-page_expected.wmf create mode 100644 testfiles/cli_tests/testcases/export-area-page_export-id.pdf create mode 100644 testfiles/cli_tests/testcases/export-area-page_export-id.png create mode 100644 testfiles/cli_tests/testcases/export-area-page_export-id.ps create mode 100644 testfiles/cli_tests/testcases/export-area-page_export-id.svg create mode 100644 testfiles/cli_tests/testcases/export-area-snap_expected.png create mode 100644 testfiles/cli_tests/testcases/export-area_expected.png create mode 100644 testfiles/cli_tests/testcases/export-background-opacity_expected.eps create mode 100644 testfiles/cli_tests/testcases/export-background-opacity_expected.pdf create mode 100644 testfiles/cli_tests/testcases/export-background-opacity_expected.png create mode 100644 testfiles/cli_tests/testcases/export-background-opacity_expected.ps create mode 100644 testfiles/cli_tests/testcases/export-background-opacity_expected.svg create mode 100644 testfiles/cli_tests/testcases/export-background_expected.emf create mode 100644 testfiles/cli_tests/testcases/export-background_expected.eps create mode 100644 testfiles/cli_tests/testcases/export-background_expected.pdf create mode 100644 testfiles/cli_tests/testcases/export-background_expected.png create mode 100644 testfiles/cli_tests/testcases/export-background_expected.ps create mode 100644 testfiles/cli_tests/testcases/export-background_expected.svg create mode 100644 testfiles/cli_tests/testcases/export-background_expected.wmf create mode 100644 testfiles/cli_tests/testcases/export-clone-group-filtered-mp.svg create mode 100644 testfiles/cli_tests/testcases/export-clone-group-filtered-mp_expected.png create mode 100644 testfiles/cli_tests/testcases/export-default-background_expected.eps create mode 100644 testfiles/cli_tests/testcases/export-default-background_expected.pdf create mode 100644 testfiles/cli_tests/testcases/export-default-background_expected.png create mode 100644 testfiles/cli_tests/testcases/export-default-background_expected.ps create mode 100644 testfiles/cli_tests/testcases/export-default-background_expected.svg create mode 100644 testfiles/cli_tests/testcases/export-dpi_expected.eps create mode 100644 testfiles/cli_tests/testcases/export-dpi_expected.pdf create mode 100644 testfiles/cli_tests/testcases/export-dpi_expected.png create mode 100644 testfiles/cli_tests/testcases/export-dpi_expected.ps create mode 100644 testfiles/cli_tests/testcases/export-filtered-clones-mp.svg create mode 100644 testfiles/cli_tests/testcases/export-filtered-clones-mp_expected.png create mode 100644 testfiles/cli_tests/testcases/export-grouped-mp.svg create mode 100644 testfiles/cli_tests/testcases/export-grouped-mp_expected.png create mode 100644 testfiles/cli_tests/testcases/export-height_expected.png create mode 100644 testfiles/cli_tests/testcases/export-id_expected.emf create mode 100644 testfiles/cli_tests/testcases/export-id_expected.eps create mode 100644 testfiles/cli_tests/testcases/export-id_expected.pdf create mode 100644 testfiles/cli_tests/testcases/export-id_expected.png create mode 100644 testfiles/cli_tests/testcases/export-id_expected.ps create mode 100644 testfiles/cli_tests/testcases/export-id_expected.svg create mode 100644 testfiles/cli_tests/testcases/export-id_expected.wmf create mode 100644 testfiles/cli_tests/testcases/export-id_export-id-only_expected.emf create mode 100644 testfiles/cli_tests/testcases/export-id_export-id-only_expected.eps create mode 100644 testfiles/cli_tests/testcases/export-id_export-id-only_expected.pdf create mode 100644 testfiles/cli_tests/testcases/export-id_export-id-only_expected.png create mode 100644 testfiles/cli_tests/testcases/export-id_export-id-only_expected.ps create mode 100644 testfiles/cli_tests/testcases/export-id_export-id-only_expected.svg create mode 100644 testfiles/cli_tests/testcases/export-id_export-id-only_expected.wmf create mode 100644 testfiles/cli_tests/testcases/export-id_export-id-only_export-area-drawing_expected.emf create mode 100644 testfiles/cli_tests/testcases/export-id_export-id-only_export-area-drawing_expected.eps create mode 100644 testfiles/cli_tests/testcases/export-id_export-id-only_export-area-drawing_expected.pdf create mode 100644 testfiles/cli_tests/testcases/export-id_export-id-only_export-area-drawing_expected.png create mode 100644 testfiles/cli_tests/testcases/export-id_export-id-only_export-area-drawing_expected.ps create mode 100644 testfiles/cli_tests/testcases/export-id_export-id-only_export-area-drawing_expected.svg create mode 100644 testfiles/cli_tests/testcases/export-id_export-id-only_export-area-drawing_expected.wmf create mode 100644 testfiles/cli_tests/testcases/export-id_export-id-only_export-area-page_expected.emf create mode 100644 testfiles/cli_tests/testcases/export-id_export-id-only_export-area-page_expected.eps create mode 100644 testfiles/cli_tests/testcases/export-id_export-id-only_export-area-page_expected.pdf create mode 100644 testfiles/cli_tests/testcases/export-id_export-id-only_export-area-page_expected.png create mode 100644 testfiles/cli_tests/testcases/export-id_export-id-only_export-area-page_expected.ps create mode 100644 testfiles/cli_tests/testcases/export-id_export-id-only_export-area-page_expected.svg create mode 100644 testfiles/cli_tests/testcases/export-id_export-id-only_export-area-page_expected.wmf create mode 100644 testfiles/cli_tests/testcases/export-ignore-filters_expected.emf create mode 100644 testfiles/cli_tests/testcases/export-ignore-filters_expected.eps create mode 100644 testfiles/cli_tests/testcases/export-ignore-filters_expected.pdf create mode 100644 testfiles/cli_tests/testcases/export-ignore-filters_expected.png create mode 100644 testfiles/cli_tests/testcases/export-ignore-filters_expected.ps create mode 100644 testfiles/cli_tests/testcases/export-ignore-filters_expected.svg create mode 100644 testfiles/cli_tests/testcases/export-ignore-filters_expected.wmf create mode 100644 testfiles/cli_tests/testcases/export-margin_drawing_expected.emf create mode 100644 testfiles/cli_tests/testcases/export-margin_drawing_expected.eps create mode 100644 testfiles/cli_tests/testcases/export-margin_drawing_expected.pdf create mode 100644 testfiles/cli_tests/testcases/export-margin_drawing_expected.png create mode 100644 testfiles/cli_tests/testcases/export-margin_drawing_expected.ps create mode 100644 testfiles/cli_tests/testcases/export-margin_drawing_expected.svg create mode 100644 testfiles/cli_tests/testcases/export-margin_drawing_expected.wmf create mode 100644 testfiles/cli_tests/testcases/export-margin_export-area_expected.png create mode 100644 testfiles/cli_tests/testcases/export-margin_export-id_expected.emf create mode 100644 testfiles/cli_tests/testcases/export-margin_export-id_expected.eps create mode 100644 testfiles/cli_tests/testcases/export-margin_export-id_expected.pdf create mode 100644 testfiles/cli_tests/testcases/export-margin_export-id_expected.png create mode 100644 testfiles/cli_tests/testcases/export-margin_export-id_expected.ps create mode 100644 testfiles/cli_tests/testcases/export-margin_export-id_expected.svg create mode 100644 testfiles/cli_tests/testcases/export-margin_export-id_expected.wmf create mode 100644 testfiles/cli_tests/testcases/export-margin_export-id_export-id-only_expected.emf create mode 100644 testfiles/cli_tests/testcases/export-margin_export-id_export-id-only_expected.eps create mode 100644 testfiles/cli_tests/testcases/export-margin_export-id_export-id-only_expected.pdf create mode 100644 testfiles/cli_tests/testcases/export-margin_export-id_export-id-only_expected.png create mode 100644 testfiles/cli_tests/testcases/export-margin_export-id_export-id-only_expected.ps create mode 100644 testfiles/cli_tests/testcases/export-margin_export-id_export-id-only_expected.svg create mode 100644 testfiles/cli_tests/testcases/export-margin_export-id_export-id-only_expected.wmf create mode 100644 testfiles/cli_tests/testcases/export-margin_mm_expected.emf create mode 100644 testfiles/cli_tests/testcases/export-margin_mm_expected.eps create mode 100644 testfiles/cli_tests/testcases/export-margin_mm_expected.pdf create mode 100644 testfiles/cli_tests/testcases/export-margin_mm_expected.png create mode 100644 testfiles/cli_tests/testcases/export-margin_mm_expected.ps create mode 100644 testfiles/cli_tests/testcases/export-margin_mm_expected.svg create mode 100644 testfiles/cli_tests/testcases/export-margin_mm_expected.wmf create mode 100644 testfiles/cli_tests/testcases/export-margin_mm_viewbox_drawing_expected.emf create mode 100644 testfiles/cli_tests/testcases/export-margin_mm_viewbox_drawing_expected.eps create mode 100644 testfiles/cli_tests/testcases/export-margin_mm_viewbox_drawing_expected.pdf create mode 100644 testfiles/cli_tests/testcases/export-margin_mm_viewbox_drawing_expected.png create mode 100644 testfiles/cli_tests/testcases/export-margin_mm_viewbox_drawing_expected.ps create mode 100644 testfiles/cli_tests/testcases/export-margin_mm_viewbox_drawing_expected.svg create mode 100644 testfiles/cli_tests/testcases/export-margin_mm_viewbox_drawing_expected.wmf create mode 100644 testfiles/cli_tests/testcases/export-margin_mm_viewbox_id_expected.emf create mode 100644 testfiles/cli_tests/testcases/export-margin_mm_viewbox_id_expected.eps create mode 100644 testfiles/cli_tests/testcases/export-margin_mm_viewbox_id_expected.pdf create mode 100644 testfiles/cli_tests/testcases/export-margin_mm_viewbox_id_expected.png create mode 100644 testfiles/cli_tests/testcases/export-margin_mm_viewbox_id_expected.ps create mode 100644 testfiles/cli_tests/testcases/export-margin_mm_viewbox_id_expected.svg create mode 100644 testfiles/cli_tests/testcases/export-margin_mm_viewbox_id_expected.wmf create mode 100644 testfiles/cli_tests/testcases/export-margin_mm_viewbox_page_expected.emf create mode 100644 testfiles/cli_tests/testcases/export-margin_mm_viewbox_page_expected.eps create mode 100644 testfiles/cli_tests/testcases/export-margin_mm_viewbox_page_expected.pdf create mode 100644 testfiles/cli_tests/testcases/export-margin_mm_viewbox_page_expected.png create mode 100644 testfiles/cli_tests/testcases/export-margin_mm_viewbox_page_expected.ps create mode 100644 testfiles/cli_tests/testcases/export-margin_mm_viewbox_page_expected.svg create mode 100644 testfiles/cli_tests/testcases/export-margin_mm_viewbox_page_expected.wmf create mode 100644 testfiles/cli_tests/testcases/export-margin_px_expected.emf create mode 100644 testfiles/cli_tests/testcases/export-margin_px_expected.eps create mode 100644 testfiles/cli_tests/testcases/export-margin_px_expected.pdf create mode 100644 testfiles/cli_tests/testcases/export-margin_px_expected.png create mode 100644 testfiles/cli_tests/testcases/export-margin_px_expected.ps create mode 100644 testfiles/cli_tests/testcases/export-margin_px_expected.svg create mode 100644 testfiles/cli_tests/testcases/export-margin_px_expected.wmf create mode 100644 testfiles/cli_tests/testcases/export-marker-on-clone.svg create mode 100644 testfiles/cli_tests/testcases/export-marker-on-clone_expected.png create mode 100644 testfiles/cli_tests/testcases/export-png-color-mode-gray-8_expected.png create mode 100644 testfiles/cli_tests/testcases/export-png-color-mode-rgb-8_expected.png create mode 100644 testfiles/cli_tests/testcases/export-png-color-mode-rgba-8_expected.png create mode 100644 testfiles/cli_tests/testcases/export-ps-level-2_expected.ps create mode 100644 testfiles/cli_tests/testcases/export-ps-level-3_expected.ps create mode 100644 testfiles/cli_tests/testcases/export-text-paintorder.svg create mode 100644 testfiles/cli_tests/testcases/export-text-paintorder_expected.png create mode 100644 testfiles/cli_tests/testcases/export-width_expected.png create mode 100644 testfiles/cli_tests/testcases/export-with-filters-multipage.svg create mode 100644 testfiles/cli_tests/testcases/export-with-filters-multipage_expected.png create mode 100644 testfiles/cli_tests/testcases/export-with-filters_expected.emf create mode 100644 testfiles/cli_tests/testcases/export-with-filters_expected.eps create mode 100644 testfiles/cli_tests/testcases/export-with-filters_expected.pdf create mode 100644 testfiles/cli_tests/testcases/export-with-filters_expected.png create mode 100644 testfiles/cli_tests/testcases/export-with-filters_expected.ps create mode 100644 testfiles/cli_tests/testcases/export-with-filters_expected.svg create mode 100644 testfiles/cli_tests/testcases/export-with-filters_expected.wmf create mode 100644 testfiles/cli_tests/testcases/export_hints.svg create mode 100644 testfiles/cli_tests/testcases/filter.svg create mode 100644 testfiles/cli_tests/testcases/gradient.svg create mode 100644 testfiles/cli_tests/testcases/lambda-background.svg create mode 100644 testfiles/cli_tests/testcases/lambda.svg create mode 100644 testfiles/cli_tests/testcases/librevenge_formats/corel_draw.cdr create mode 100644 testfiles/cli_tests/testcases/librevenge_formats/corel_draw2.cdr create mode 100644 testfiles/cli_tests/testcases/librevenge_formats/corel_draw2_expected.png create mode 100644 testfiles/cli_tests/testcases/librevenge_formats/corel_draw_expected.png create mode 100644 testfiles/cli_tests/testcases/librevenge_formats/visio.vsd create mode 100644 testfiles/cli_tests/testcases/librevenge_formats/visio.vsd_expected.png create mode 100644 testfiles/cli_tests/testcases/librevenge_formats/visio.vsdx create mode 100644 testfiles/cli_tests/testcases/librevenge_formats/visio.vsdx_expected.png create mode 100644 testfiles/cli_tests/testcases/librevenge_formats/word_perfect.wpg create mode 100644 testfiles/cli_tests/testcases/librevenge_formats/word_perfect_expected.png create mode 100644 testfiles/cli_tests/testcases/multiline-anchoring.svg create mode 100644 testfiles/cli_tests/testcases/offset.svg create mode 100644 testfiles/cli_tests/testcases/path.svg create mode 100644 testfiles/cli_tests/testcases/pdf-mesh.pdf create mode 100644 testfiles/cli_tests/testcases/pdf-pages.pdf create mode 100644 testfiles/cli_tests/testcases/pdfinput/cms.pdf create mode 100644 testfiles/cli_tests/testcases/pdfinput/cms_expected.svg create mode 100644 testfiles/cli_tests/testcases/pdfinput/font-spacing.pdf create mode 100644 testfiles/cli_tests/testcases/pdfinput/font-spacing_expected.svg create mode 100644 testfiles/cli_tests/testcases/pdfinput/font-style.pdf create mode 100644 testfiles/cli_tests/testcases/pdfinput/font-style_expected.svg create mode 100644 testfiles/cli_tests/testcases/pdfinput/gradients.pdf create mode 100644 testfiles/cli_tests/testcases/pdfinput/gradients_expected.svg create mode 100644 testfiles/cli_tests/testcases/pdfinput/latex.pdf create mode 100644 testfiles/cli_tests/testcases/pdfinput/latex_expected.svg create mode 100644 testfiles/cli_tests/testcases/pdfinput/multi-page-sample.pdf create mode 100644 testfiles/cli_tests/testcases/pdfinput/multi-page-sample_expected.svg create mode 100644 testfiles/cli_tests/testcases/pdfinput/rotated-gradients.pdf create mode 100644 testfiles/cli_tests/testcases/pdfinput/rotated-gradients_expected.svg create mode 100644 testfiles/cli_tests/testcases/pdfinput/transparency-groups.pdf create mode 100644 testfiles/cli_tests/testcases/pdfinput/transparency-groups_expected.svg create mode 100644 testfiles/cli_tests/testcases/pyramids.svg create mode 100644 testfiles/cli_tests/testcases/rects.svg create mode 100644 testfiles/cli_tests/testcases/regression-1364.svg create mode 100644 testfiles/cli_tests/testcases/regression-1364_script.py create mode 100644 testfiles/cli_tests/testcases/regression-2602.svg create mode 100644 testfiles/cli_tests/testcases/regression-2602_script.py create mode 100644 testfiles/cli_tests/testcases/regression-2797.svg create mode 100644 testfiles/cli_tests/testcases/regression-2797_script.py create mode 100644 testfiles/cli_tests/testcases/shapes.svg create mode 100644 testfiles/cli_tests/testcases/shapes_expected.emf create mode 100644 testfiles/cli_tests/testcases/shapes_expected.eps create mode 100644 testfiles/cli_tests/testcases/shapes_expected.pdf create mode 100644 testfiles/cli_tests/testcases/shapes_expected.png create mode 100644 testfiles/cli_tests/testcases/shapes_expected.ps create mode 100644 testfiles/cli_tests/testcases/shapes_expected.wmf create mode 100644 testfiles/cli_tests/testcases/shapes_expected.xaml create mode 100644 testfiles/cli_tests/testcases/shapes_pre_0.92.svg create mode 100644 testfiles/cli_tests/testcases/shapes_pre_0.92_scaled_expected.png create mode 100644 testfiles/cli_tests/testcases/square_mm.svg create mode 100644 testfiles/cli_tests/testcases/square_mm_viewbox.svg create mode 100644 testfiles/cli_tests/testcases/square_px.svg create mode 100644 testfiles/cli_tests/testcases/stroke-to-path-variations.svg create mode 100644 testfiles/cli_tests/testcases/systemLanguage.svg create mode 100644 testfiles/cli_tests/testcases/systemLanguage_RDF.svg create mode 100644 testfiles/cli_tests/testcases/systemLanguage_de.png create mode 100644 testfiles/cli_tests/testcases/systemLanguage_default.png create mode 100644 testfiles/cli_tests/testcases/systemLanguage_en.png create mode 100644 testfiles/cli_tests/testcases/systemLanguage_fr.png create mode 100644 testfiles/cli_tests/testcases/systemLanguage_pt.png create mode 100644 testfiles/cli_tests/testcases/text.svg create mode 100644 testfiles/cli_tests/testcases/text_in_group.svg create mode 100644 testfiles/cli_tests/testcases/theta.svg create mode 100644 testfiles/data/crystalegg.xml create mode 100644 testfiles/data/doc2html.xsl create mode 100644 testfiles/data/example-FEXTRA-FCOMMENT.gz create mode 100644 testfiles/data/visual-bounds.svg create mode 100644 testfiles/doc-per-case-test.cpp create mode 100644 testfiles/doc-per-case-test.h create mode 100644 testfiles/fuzzer.cpp create mode 100644 testfiles/fuzzer.dict create mode 100644 testfiles/lpe_tests/CMakeLists.txt create mode 100755 testfiles/lpe_tests/Inkscape_0_92.svg create mode 100755 testfiles/lpe_tests/Inkscape_0_92_64.svg create mode 100644 testfiles/lpe_tests/Inkscape_1_0.svg create mode 100644 testfiles/lpe_tests/Inkscape_1_0_64.svg create mode 100644 testfiles/lpe_tests/Inkscape_1_1.svg create mode 100644 testfiles/lpe_tests/Inkscape_1_2.svg create mode 100644 testfiles/lpe_tests/Inkscape_1_3.svg create mode 100644 testfiles/lpe_tests/README create mode 100644 testfiles/lpespaths-test.h create mode 100644 testfiles/rendering_tests/CMakeLists.txt create mode 100644 testfiles/rendering_tests/README create mode 100644 testfiles/rendering_tests/drawing-pattern-test.svg create mode 100644 testfiles/rendering_tests/expected_rendering/multi-style.png create mode 100644 testfiles/rendering_tests/expected_rendering/selector-important-002-large.png create mode 100644 testfiles/rendering_tests/expected_rendering/selector-important-002.png create mode 100644 testfiles/rendering_tests/expected_rendering/selector-important-003-large.png create mode 100644 testfiles/rendering_tests/expected_rendering/selector-important-003.png create mode 100644 testfiles/rendering_tests/expected_rendering/style-parsing.png create mode 100644 testfiles/rendering_tests/expected_rendering/symbol-svg2-geometry-properties.png create mode 100644 testfiles/rendering_tests/expected_rendering/symbol-svg2-ref.png create mode 100644 testfiles/rendering_tests/expected_rendering/test-baseline-shift-large.png create mode 100644 testfiles/rendering_tests/expected_rendering/test-baseline-shift.png create mode 100644 testfiles/rendering_tests/expected_rendering/test-dont-crash.png create mode 100644 testfiles/rendering_tests/expected_rendering/test-empty-large.png create mode 100644 testfiles/rendering_tests/expected_rendering/test-empty.png create mode 100644 testfiles/rendering_tests/expected_rendering/test-glyph-y-pos-large.png create mode 100644 testfiles/rendering_tests/expected_rendering/test-glyph-y-pos.png create mode 100644 testfiles/rendering_tests/expected_rendering/test-powerstroke-join-large.png create mode 100644 testfiles/rendering_tests/expected_rendering/test-powerstroke-join.png create mode 100644 testfiles/rendering_tests/expected_rendering/test-rtl-vertical-large.png create mode 100644 testfiles/rendering_tests/expected_rendering/test-rtl-vertical.png create mode 100644 testfiles/rendering_tests/expected_rendering/test-use-large.png create mode 100644 testfiles/rendering_tests/expected_rendering/test-use.png create mode 100644 testfiles/rendering_tests/expected_rendering/text-flow.png create mode 100644 testfiles/rendering_tests/expected_rendering/text-glyphs-combining-large.png create mode 100644 testfiles/rendering_tests/expected_rendering/text-glyphs-combining.png create mode 100644 testfiles/rendering_tests/expected_rendering/text-glyphs-vertical-large.png create mode 100644 testfiles/rendering_tests/expected_rendering/text-glyphs-vertical.png create mode 100644 testfiles/rendering_tests/expected_rendering/text-gzipped-svg-glyph.png create mode 100644 testfiles/rendering_tests/expected_rendering/text-shaping-large.png create mode 100644 testfiles/rendering_tests/expected_rendering/text-shaping.png create mode 100644 testfiles/rendering_tests/fonts/Estedad-Medium.ttf create mode 100644 testfiles/rendering_tests/fonts/GeomTest-Regular.otf create mode 100755 testfiles/rendering_tests/fonts/GeomTest-gzipped-SVG-glyphs.otf create mode 100644 testfiles/rendering_tests/fonts/LICENSES create mode 100644 testfiles/rendering_tests/fonts/Lohit-Telugu.ttf create mode 100644 testfiles/rendering_tests/fonts/NotoSans-Regular.ttf create mode 100644 testfiles/rendering_tests/fonts/NotoSansCJKjp-Regular.otf create mode 100644 testfiles/rendering_tests/fonts/NotoSansHebrew-Regular.ttf create mode 100644 testfiles/rendering_tests/multi-style-import-1.css create mode 100644 testfiles/rendering_tests/multi-style-import-2.css create mode 100644 testfiles/rendering_tests/multi-style.svg create mode 100644 testfiles/rendering_tests/selector-important-002.svg create mode 100644 testfiles/rendering_tests/selector-important-003.svg create mode 100644 testfiles/rendering_tests/style-parsing.svg create mode 100644 testfiles/rendering_tests/symbol-svg2-geometry-properties.svg create mode 100644 testfiles/rendering_tests/symbol-svg2-ref.svg create mode 100644 testfiles/rendering_tests/test-baseline-shift.svg create mode 100644 testfiles/rendering_tests/test-dont-crash.svg create mode 100644 testfiles/rendering_tests/test-empty.svg create mode 100644 testfiles/rendering_tests/test-glyph-y-pos.svg create mode 100644 testfiles/rendering_tests/test-powerstroke-join.svg create mode 100644 testfiles/rendering_tests/test-rtl-vertical.svg create mode 100644 testfiles/rendering_tests/test-use-ref.svg create mode 100644 testfiles/rendering_tests/test-use.svg create mode 100755 testfiles/rendering_tests/test.sh create mode 100644 testfiles/rendering_tests/text-flow.svg create mode 100644 testfiles/rendering_tests/text-glyphs-combining.svg create mode 100644 testfiles/rendering_tests/text-glyphs-vertical.svg create mode 100644 testfiles/rendering_tests/text-gzipped-svg-glyph.svg create mode 100644 testfiles/rendering_tests/text-shaping.svg create mode 100644 testfiles/src/2geom-characterization-test.cpp create mode 100644 testfiles/src/async_channel-test.cpp create mode 100644 testfiles/src/async_funclog-test.cpp create mode 100644 testfiles/src/async_progress-test.cpp create mode 100644 testfiles/src/attributes-test.cpp create mode 100644 testfiles/src/cairo-utils-test.cpp create mode 100644 testfiles/src/color-profile-test.cpp create mode 100644 testfiles/src/curve-test.cpp create mode 100644 testfiles/src/cxxtests-to-migrate/marker-test.h create mode 100644 testfiles/src/cxxtests-to-migrate/mod360-test.h create mode 100644 testfiles/src/cxxtests-to-migrate/preferences-test.h create mode 100644 testfiles/src/cxxtests-to-migrate/sp-style-elem-test.h create mode 100644 testfiles/src/cxxtests-to-migrate/test-helpers.h create mode 100644 testfiles/src/cxxtests-to-migrate/verbs-test.h create mode 100644 testfiles/src/dir-util-test.cpp create mode 100644 testfiles/src/drag-and-drop-svgz.cpp create mode 100644 testfiles/src/drawing-pattern-test.cpp create mode 100644 testfiles/src/extract-uri-test.cpp create mode 100644 testfiles/src/lpe-test.cpp create mode 100644 testfiles/src/lpe64-test.cpp create mode 100644 testfiles/src/min-bbox-test.cpp create mode 100644 testfiles/src/object-set-test.cpp create mode 100644 testfiles/src/object-style-test.cpp create mode 100644 testfiles/src/object-test.cpp create mode 100644 testfiles/src/oklab-color-test.cpp create mode 100644 testfiles/src/path-boolop-test.cpp create mode 100644 testfiles/src/path-reverse-lpe-test.cpp create mode 100644 testfiles/src/rebase-hrefs-test.cpp create mode 100644 testfiles/src/sp-glyph-kerning-test.cpp create mode 100644 testfiles/src/sp-gradient-test.cpp create mode 100644 testfiles/src/sp-item-group-test.cpp create mode 100644 testfiles/src/sp-object-tags-test.cpp create mode 100644 testfiles/src/sp-object-test.cpp create mode 100644 testfiles/src/stream-test.cpp create mode 100644 testfiles/src/style-elem-test.cpp create mode 100644 testfiles/src/style-internal-test.cpp create mode 100644 testfiles/src/style-test.cpp create mode 100644 testfiles/src/svg-affine-test.cpp create mode 100644 testfiles/src/svg-box-test.cpp create mode 100644 testfiles/src/svg-color-test.cpp create mode 100644 testfiles/src/svg-extension-test.cpp create mode 100644 testfiles/src/svg-length-test.cpp create mode 100644 testfiles/src/svg-path-geom-test.cpp create mode 100644 testfiles/src/svg-stringstream-test.cpp create mode 100644 testfiles/src/uri-test.cpp create mode 100644 testfiles/src/util-test.cpp create mode 100644 testfiles/src/visual-bounds-test.cpp create mode 100644 testfiles/src/xml-test.cpp create mode 100644 testfiles/unittest.cpp create mode 100644 testfiles/utils/functions.sh (limited to 'testfiles') diff --git a/testfiles/CMakeLists.txt b/testfiles/CMakeLists.txt new file mode 100644 index 0000000..dc0cbd6 --- /dev/null +++ b/testfiles/CMakeLists.txt @@ -0,0 +1,137 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# ----------------------------------------------------------------------------- + +# custom "check" target with proper dependencies (builds inkscape and tests) +file(TO_NATIVE_PATH "/" _separator) +ADD_DEFINITIONS(-DINKSCAPE_TESTS_DIR="${CMAKE_SOURCE_DIR}/testfiles") +add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure + DEPENDS tests + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) +add_dependencies(check inkscape) + + +# create symlink "inkscape_datadir" to use as INKSCAPE_DATADIR +# - ensures tests can be run without installing the project +# - also helpful for running Inkscape uninstalled: 'INKSVAPE_DATADIR=inkscape_datadir bin/inkscape' +set(INKSCAPE_DATADIR ${CMAKE_BINARY_DIR}/inkscape_datadir) +if(NOT EXISTS ${INKSCAPE_DATADIR}/inkscape) + set(link_source ${INKSCAPE_DATADIR}/inkscape) + set(link_target ${CMAKE_SOURCE_DIR}/share) + message(STATUS "Creating link '${link_source}' --> '${link_target}'") + execute_process(COMMAND mkdir inkscape_datadir) + execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink ${link_target} ${link_source} + RESULT_VARIABLE result) + if(result) + message(WARNING "Creation of link failed: ${result}") + endif() +endif() +# check if creation succeeded +if(EXISTS ${INKSCAPE_DATADIR}/inkscape) + set(CMAKE_CTEST_ENV INKSCAPE_DATADIR=${INKSCAPE_DATADIR}) +else() + message(WARNING "Directory 'inkscape_datadir/inkscape' missing. Tests might not run properly.\n" + "Possible solutions:\n" + " - create a suitable symlink yourself, e.g.\n" + " ln -s ${CMAKE_SOURCE_DIR}/share ${INKSCAPE_DATADIR}/inkscape\n" + " - run '${CMAKE_MAKE_PROGRAM} install' before running tests (only for not relocatable packages.\n" + " - set the environment variable 'INKSCAPE_DATADIR' manually (every time you run tests)") +endif() + + +# Set custom profile directory for tests using environment variable. +# Copy CTestCustom.cmake into binary dir, where it will be picked up automatically by ctest for cleanup. +set(INKSCAPE_TEST_PROFILE_DIR ${CMAKE_CURRENT_BINARY_DIR}/test_profile_dir) +set(INKSCAPE_TEST_PROFILE_DIR_ENV INKSCAPE_PROFILE_DIR=${INKSCAPE_TEST_PROFILE_DIR}) +configure_file(CTestCustom.cmake.in ${CMAKE_BINARY_DIR}/CTestCustom.cmake) + + + +### tests using gtest +include_directories("${CMAKE_SOURCE_DIR}/src/3rdparty/adaptagrams") # TODO: remove this hack + +if(${CMAKE_SIZEOF_VOID_P} EQUAL 8) + set(LPE_TESTS_64bit + #0.92 or lower LPEs + # (test not stable on 32bit Windows) + lpe64-test + ) +endif() + +set(TEST_SOURCES + async_channel-test + async_funclog-test + async_progress-test + uri-test + util-test + drag-and-drop-svgz + drawing-pattern-test + extract-uri-test + attributes-test + color-profile-test + dir-util-test + min-bbox-test + oklab-color-test + sp-object-test + sp-object-tags-test + object-set-test + object-style-test + path-boolop-test + path-reverse-lpe-test + rebase-hrefs-test + stream-test + style-elem-test + style-internal-test + style-test + svg-affine-test + svg-box-test + svg-color-test + svg-length-test + svg-stringstream-test + sp-gradient-test + svg-path-geom-test + visual-bounds-test + object-test + sp-glyph-kerning-test + cairo-utils-test + svg-extension-test + curve-test + 2geom-characterization-test + xml-test + sp-item-group-test + lpe-test + ${LPE_TESTS_64bit} + ) + +add_library(cpp_test_static_library SHARED unittest.cpp doc-per-case-test.cpp lpespaths-test.h) +target_link_libraries(cpp_test_static_library PUBLIC ${GTEST_LIBRARIES} inkscape_base) + +add_custom_target(tests) +foreach(test_source ${TEST_SOURCES}) + string(REPLACE "-test" "" testname "test_${test_source}") + add_executable(${testname} src/${test_source}.cpp) + target_include_directories(${testname} SYSTEM PRIVATE ${GTEST_INCLUDE_DIRS}) + target_link_libraries(${testname} cpp_test_static_library 2Geom::2geom) + add_test(NAME ${testname} COMMAND ${testname}) + set_tests_properties(${testname} PROPERTIES ENVIRONMENT "${INKSCAPE_TEST_PROFILE_DIR_ENV}/${testname};${CMAKE_CTEST_ENV}") + add_dependencies(tests ${testname}) +endforeach() + + +### CLI rendering tests and LPE +add_subdirectory(cli_tests) +add_subdirectory(rendering_tests) +add_subdirectory(lpe_tests) + +### Fuzz test +if(WITH_FUZZ) + # to use the fuzzer, make sure you use the right compiler (clang) + # with the right flags -fsanitize=address -fsanitize-coverage=edge,trace-pc-guard,indirect-calls,trace-cmp,trace-div,trace-gep -fno-omit-frame-pointer + # (see libfuzzer doc for info in flags) + # first line is for integration into oss-fuzz https://github.com/google/oss-fuzz + add_executable(fuzz fuzzer.cpp) + if(LIB_FUZZING_ENGINE) + target_link_libraries(fuzz inkscape_base -lFuzzingEngine) + else() + target_link_libraries(fuzz inkscape_base -lFuzzer) + endif() +endif() diff --git a/testfiles/CTestCustom.cmake.in b/testfiles/CTestCustom.cmake.in new file mode 100644 index 0000000..9c1c834 --- /dev/null +++ b/testfiles/CTestCustom.cmake.in @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +# Cleanup test-specific profile directories whenever running tests (before and after, just to be safe) +set(CTEST_CUSTOM_PRE_TEST "rm -rf ${INKSCAPE_TEST_PROFILE_DIR}") +set(CTEST_CUSTOM_POST_TEST "rm -rf ${INKSCAPE_TEST_PROFILE_DIR}") diff --git a/testfiles/cli_tests/CMakeLists.txt b/testfiles/cli_tests/CMakeLists.txt new file mode 100644 index 0000000..439bf2c --- /dev/null +++ b/testfiles/cli_tests/CMakeLists.txt @@ -0,0 +1,1006 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + + +# Helper function to add a CLI test +# +# Run an Inkscape command line and check for pass/fail condition (by default only exit status is checked) +# +# Command line options: +# INPUT_FILENAME - name of input file (optional) +# OUTPUT_FILENAME - name of output file (optional) +# OUTPUT_PAGE - index of page in multipage output (optional), starts from 0 +# FUZZ_PERCENTAGE - maximum allowed normalized root-mean-squared distance between compared images +# RASTER_DPI - DPI setting for rasterizing vector formats before root-mean-squared comparison +# PARAMETERS - additional command line parameters to pass to Inkscape +# +# Pass/fail criteria: +# PASS_FOR_OUTPUT - pass if output matches the given value, otherwise fail +# see https://cmake.org/cmake/help/latest/prop_test/PASS_REGULAR_EXPRESSION.html for details +# FAIL_FOR_OUTPUT - fail if output matches the given value +# see https://cmake.org/cmake/help/latest/prop_test/FAIL_REGULAR_EXPRESSION.html for details +# REFERENCE_FILENAME - compare OUTPUT_FILENAME with this pre-rendered reference file +# both files are converted to PNG and compared with ImageMagick's 'compare' +# for multipage output, use OUTPUT_PAGE to specify a single page for comparison +# FUZZYREF_FILENAME - comparison of OUTPUT_FILENAME with this pre-rendered reference file will be +# performed in the L2 metric, subject to the specified FUZZ_PERCENTAGE +# EXPECTED_FILES - verify the command produced the expected files (i.e. they exist on disk) +# TEST_SCRIPT - additional script to run after performing all checks and before cleaning up +# +# Other options: +# ENVIRONMENT - Additional environment variables to set while running the test +function(add_cli_test name) + # parse arguments + set(oneValueArgs INPUT_FILENAME OUTPUT_FILENAME OUTPUT_PAGE PASS_FOR_OUTPUT FAIL_FOR_OUTPUT REFERENCE_FILENAME + FUZZYREF_FILENAME FUZZ_PERCENTAGE RASTER_DPI) + set(multiValueArgs PARAMETERS EXPECTED_FILES TEST_SCRIPT ENVIRONMENT) + cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + set(testname cli_${name}) + + #disable dithering + set(ARG_PARAMETERS ${ARG_PARAMETERS} "--export-png-use-dithering=false") + + if(DEFINED ARG_OUTPUT_FILENAME) + # for actions: if an export-do is present do not append export-filename option to prevent + if(NOT ARG_PARAMETERS MATCHES "export-do") + set(ARG_PARAMETERS ${ARG_PARAMETERS} "--export-filename=${ARG_OUTPUT_FILENAME}") + endif() + endif() + if(DEFINED ARG_INPUT_FILENAME) + set(ARG_INPUT_FILENAME "${CMAKE_CURRENT_SOURCE_DIR}/testcases/${ARG_INPUT_FILENAME}") + set(ARG_PARAMETERS ${ARG_PARAMETERS} ${ARG_INPUT_FILENAME}) + endif() + + set(CMAKE_CTEST_ENV "${INKSCAPE_TEST_PROFILE_DIR_ENV}/${testname};${CMAKE_CTEST_ENV}") + if(DEFINED ARG_ENVIRONMENT) + if(ARG_ENVIRONMENT STREQUAL "unset") + unset(CMAKE_CTEST_ENV) + else() + # variables might already be set, however the last value wins + list(APPEND CMAKE_CTEST_ENV ${ARG_ENVIRONMENT}) + endif() + endif() + + # add test for main command line + add_test(NAME ${testname} COMMAND inkscape ${ARG_PARAMETERS}) + set_tests_properties(${testname} PROPERTIES ENVIRONMENT "${CMAKE_CTEST_ENV}") + if(DEFINED ARG_PASS_FOR_OUTPUT) + set_tests_properties(${testname} PROPERTIES PASS_REGULAR_EXPRESSION ${ARG_PASS_FOR_OUTPUT}) + endif() + if(DEFINED ARG_FAIL_FOR_OUTPUT) + set_tests_properties(${testname} PROPERTIES FAIL_REGULAR_EXPRESSION ${ARG_FAIL_FOR_OUTPUT}) + endif() + + # add test to check output files + if(DEFINED ARG_REFERENCE_FILENAME OR DEFINED ARG_EXPECTED_FILES OR DEFINED ARG_TEST_SCRIPT) + if(DEFINED ARG_REFERENCE_FILENAME) + file(TO_NATIVE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/testcases/${ARG_REFERENCE_FILENAME}" ARG_REFERENCE_FILENAME) + endif() + if(DEFINED ARG_EXPECTED_FILES) + string(REPLACE ";" " " ARG_EXPECTED_FILES "${ARG_EXPECTED_FILES}") + endif() + if(DEFINED ARG_TEST_SCRIPT) + set(ARG_TEST_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/${ARG_TEST_SCRIPT}") + endif() + + add_test(NAME ${testname}_check_output + COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/check_output.sh + "${ARG_OUTPUT_FILENAME}" "${ARG_OUTPUT_PAGE}" "${ARG_REFERENCE_FILENAME}" + "${ARG_EXPECTED_FILES}" "${ARG_TEST_SCRIPT}") + set_tests_properties(${testname}_check_output PROPERTIES + ENVIRONMENT "${CMAKE_CTEST_ENV}" DEPENDS ${testname} SKIP_RETURN_CODE 42) + endif() + + # add a fuzzy test to check the output files + if(DEFINED ARG_FUZZYREF_FILENAME) + file(TO_NATIVE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/testcases/${ARG_FUZZYREF_FILENAME}" ARG_FUZZYREF_FILENAME) + if(DEFINED ARG_FUZZ_PERCENTAGE) + set(ARG_FUZZ "${ARG_FUZZ_PERCENTAGE}") + else() + set(ARG_FUZZ "0") + endif() + add_test(NAME ${testname}_check_output + COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/l2compare.sh + "${ARG_OUTPUT_FILENAME}" "${ARG_OUTPUT_PAGE}" "${ARG_FUZZYREF_FILENAME}" "${ARG_FUZZ}" "${ARG_RASTER_DPI}") + set_tests_properties(${testname}_check_output PROPERTIES + ENVIRONMENT "${CMAKE_CTEST_ENV}" DEPENDS ${testname} SKIP_RETURN_CODE 42) + endif() + +endfunction(add_cli_test) + + +##### Tests follow below ##### + + + +############################################################################################# +### Command line options (basic tests for all program options as listed in --help output) ### +############################################################################################# + +# --help + +# --version (check if we can run inkscape and the revision is known) +add_cli_test(version PARAMETERS --version) +add_cli_test(version_known PARAMETERS --version FAIL_FOR_OUTPUT "\\(unknown\\)") + +# --system-data-directory / --user-data-directory (unset environment variables to override our override) +# TODO: Can we make these tests more specific without making too many assumptions? +add_cli_test(system-data-directory PARAMETERS --system-data-directory ENVIRONMENT unset PASS_FOR_OUTPUT "inkscape\n$") +add_cli_test(user-data-directory PARAMETERS --user-data-directory ENVIRONMENT unset PASS_FOR_OUTPUT "inkscape\n$") + +# --pipe + +# --pdf-page=PAGE + +# --pdf-poppler +add_cli_test(pdf-poppler-mesh-import + PARAMETERS --pdf-poppler + INPUT_FILENAME pdf-mesh.pdf + OUTPUT_FILENAME pdf-mesh_poppler.svg + TEST_SCRIPT match_regex.sh pdf-mesh_poppler.svg "file-open:${CMAKE_CURRENT_SOURCE_DIR}/testcases/shapes_pre_0.92.svg$export-filename:action-convert-dpi-method-scale-none.png$export-do + OUTPUT_FILENAME action-convert-dpi-method-scale-none.png + REFERENCE_FILENAME shapes_expected.png) +add_cli_test(action-convert-dpi-method-scale-viewbox + PARAMETERS --export-area-page --actions=convert-dpi-method:scale-viewbox$file-open:${CMAKE_CURRENT_SOURCE_DIR}/testcases/shapes_pre_0.92.svg$export-filename:action-convert-dpi-method-scale-viewbox.png$export-do + OUTPUT_FILENAME action-convert-dpi-method-scale-viewbox.png + REFERENCE_FILENAME shapes_pre_0.92_scaled_expected.png) +add_cli_test(action-convert-dpi-method-scale-document + PARAMETERS --export-area-page --actions=convert-dpi-method:scale-document$file-open:${CMAKE_CURRENT_SOURCE_DIR}/testcases/shapes_pre_0.92.svg$export-filename:action-convert-dpi-method-scale-document.png$export-do + OUTPUT_FILENAME action-convert-dpi-method-scale-document.png + REFERENCE_FILENAME shapes_pre_0.92_scaled_expected.png) + +# debug-info +add_cli_test(actions-debug-info PARAMETERS --actions=debug-info PASS_FOR_OUTPUT "OS version:") + +# export-area +# export-area-drawing +# export-area-page +# export-area-snap +# export-background +# export-background-opacity +# export-do +# export-dpi +# export-filename +# export-height +# export-id +# export-id-only +# export-ignore-filters +# export-latex +# export-margin +# export-overwrite +# export-pdf-version +# export-plain-svg +# export-png-color-mode +# export-ps-level +# export-text-to-path +# export-type +# export-use-hints +# export-width + +# file-close +add_cli_test(actions-file-close PARAMETERS --actions=file-new$file-close$file-open:${CMAKE_CURRENT_SOURCE_DIR}/testcases/theta.svg$export-filename:actions-file-close.svg$export-do + TEST_SCRIPT match_regex.sh actions-file-close.svg "id=\"purple-cover\"") + +# file-new +add_cli_test(actions-file-new1 PARAMETERS --actions=file-new$export-filename:actions-file-new1.svg$export-do + TEST_SCRIPT match_regex.sh actions-file-new1.svg "viewBox=\"0 0 210 297\"") +add_cli_test(actions-file-new2 PARAMETERS --actions=file-new:${CMAKE_CURRENT_SOURCE_DIR}/testcases/theta.svg$export-filename:actions-file-new2.svg$export-do + TEST_SCRIPT match_regex.sh actions-file-new2.svg "id=\"purple-cover\"") + +# file-open +add_cli_test(actions-file-open PARAMETERS --actions=file-open:${CMAKE_CURRENT_SOURCE_DIR}/testcases/theta.svg$export-filename:actions-file-open.png$export-do + OUTPUT_FILENAME actions-file-open.png + REFERENCE_FILENAME actions-file-open_expected.png) + +# inkscape-version +add_cli_test(actions-inkscape-version PARAMETERS --actions=inkscape-version + PASS_FOR_OUTPUT "Inkscape [0-9]+\\.[0-9]+") + +# no-convert-baseline +# object-align +add_cli_test(actions-object-align INPUT_FILENAME lambda.svg + PARAMETERS --actions=select-by-id:green$object-align:left\ page + OUTPUT_FILENAME actions-object-align.png + REFERENCE_FILENAME actions-object-align_expected.png) + +# object-distribute +add_cli_test(actions-object-distribute INPUT_FILENAME rects.svg + PARAMETERS --actions=select-by-element:rect$object-distribute:vgap + OUTPUT_FILENAME actions-object-distribute.png + REFERENCE_FILENAME actions-object-distribute_expected.png) + +# object-set-attribute +add_cli_test(actions-object-set-attribute INPUT_FILENAME rects.svg + PARAMETERS --actions=select-by-id:rect1$object-set-attribute:rx,15 + OUTPUT_FILENAME actions-object-set-attribute.png + REFERENCE_FILENAME actions-object-set-attribute_expected.png) + +# object-set-property +add_cli_test(actions-object-set-property INPUT_FILENAME areas.svg + PARAMETERS --actions=select-by-id:MyRect$object-set-property:fill,gold + OUTPUT_FILENAME actions-object-set-property.svg + TEST_SCRIPT match_regex.sh actions-object-set-property.svg "style=\"fill:gold\"") + +# path-simplify +add_cli_test(actions-path-simplify INPUT_FILENAME path.svg + PARAMETERS --actions=select-by-id:gates$path-simplify$export-id:gates$export-id-only + OUTPUT_FILENAME actions-object-simplify-path.png + REFERENCE_FILENAME actions-object-simplify-path_expected.png) + +# object-stroke-to-path +add_cli_test(actions-object-stroke-to-path INPUT_FILENAME path.svg + PARAMETERS --actions=select-by-id:cross$object-stroke-to-path$select-by-selector:path\:nth-of-type\(2\)$object-set-attribute:stroke,red$export-id:cross$export-id-only + OUTPUT_FILENAME actions-object-stroke-to-path.png + REFERENCE_FILENAME actions-object-stroke-to-path_expected.png) + +add_cli_test(action_stroke-to-path_varied + INPUT_FILENAME stroke-to-path-variations.svg + PARAMETERS --actions=select-all:all$object-stroke-to-path$export-filename:stroke-to-path-variations_out.svg$export-do + EXPECTED_FILES stroke-to-path-variations_out.svg + TEST_SCRIPT match_regex_fail.sh stroke-to-path-variations_out.svg "(#mixed)|(#fillet)|(object-to-path$export-id:MyRect$export-id-only + OUTPUT_FILENAME actions-object-to-path.svg + TEST_SCRIPT match_regex.sh actions-object-to-path.svg "object-unlink-clones$object-set-attribute:fill,red$export-id:clone$export-id-only + OUTPUT_FILENAME actions-object-unlink-clones.png + REFERENCE_FILENAME actions-object-unlink-clones_expected.png) +# open-page +add_cli_test(actions-open-page PARAMETERS --actions=open-page:3$file-open:${CMAKE_CURRENT_SOURCE_DIR}/testcases/pdf-pages.pdf$export-filename:actions-open-page.png$export-do + OUTPUT_FILENAME actions-open-page.png + REFERENCE_FILENAME actions-open-page_expected.png) + +# query-all: test = query-all +add_cli_test(actions-query-all INPUT_FILENAME rects.svg PARAMETERS --actions=select-by-id:rect2$query-all PASS_FOR_OUTPUT ${query_all_expected}) + +# query-height: test = query-height +add_cli_test(actions-query-height INPUT_FILENAME rects.svg PARAMETERS --actions=select-by-id:rect2$query-height PASS_FOR_OUTPUT 70) + +# query-width: test = query-width +add_cli_test(actions-query-width INPUT_FILENAME rects.svg PARAMETERS --actions=select-by-id:rect2$query-width PASS_FOR_OUTPUT 80) + +# query-x: test = query-x +add_cli_test(actions-query-x INPUT_FILENAME rects.svg PARAMETERS --actions=select-by-id:rect2$query-x PASS_FOR_OUTPUT 110) + +# query-y: test = query-y +add_cli_test(actions-query-y INPUT_FILENAME rects.svg PARAMETERS --actions=select-by-id:rect2$query-y PASS_FOR_OUTPUT 20) + +# quit-inkscape +# Failed because of https://gitlab.com/inkscape/inkscape/-/issues/1565 +# add_cli_test(actions-quit-inkscape PARAMETERS --actions=quit-inkscape FAIL_FOR_OUTPUT ".+") + +# select: it is deprecated +# select-all +# Failed because of https://gitlab.com/inkscape/inkscape/-/issues/1565 +# add_cli_test(actions-select-all INPUT_FILENAME path.svg PARAMETERS --actions=select-all$select-list +# PASS_FOR_OUTPUT "^heart .+\nbottom-left-corner .+\nsquare .+\ntop-right-corner .+\narc-big .+\narc-small .+\ngates .+\ncross .+\nclone .+$") + +# select-by-class +# Failed because of https://gitlab.com/inkscape/inkscape/-/issues/1565 +# add_cli_test(actions-select-by-class INPUT_FILENAME path.svg PARAMETERS --actions=select-by-class:corner$select-list +# PASS_FOR_OUTPUT "^bottom-left-corner .+\ntop-right-corner .+$") + +# select-by-element +# Failed because of https://gitlab.com/inkscape/inkscape/-/issues/1565 +# add_cli_test(actions-select-by-element INPUT_FILENAME path.svg PARAMETERS --actions=select-by-element:use$select-list +# PASS_FOR_OUTPUT "^clone .+$") + +# select-by-id +# Failed because of https://gitlab.com/inkscape/inkscape/-/issues/1565 +# add_cli_test(actions-select-by-id INPUT_FILENAME path.svg PARAMETERS --actions=select-by-id:top-right-corner$select-list +# PASS_FOR_OUTPUT "^top-right-corner .+$") + +# select-by-selector = svg > path:nth-child(2n) ~ *[fill=red]:nth-of-type(even) +# Failed because of https://gitlab.com/inkscape/inkscape/-/issues/1565 +# add_cli_test(actions-select-by-selector INPUT_FILENAME path.svg PARAMETERS --actions=select-by-selector:svg\ >\ path:nth-child\(2n\)\ ~\ *\[fill\=red\]:nth-of-type\(even\)$select-list +# PASS_FOR_OUTPUT "^arc-big .+\nclone .+$") + +# select-clear +# Failed because of https://gitlab.com/inkscape/inkscape/-/issues/1565 +# add_cli_test(actions-select-clear INPUT_FILENAME path.svg PARAMETERS --actions=select-by-element:path$select-clear$select-by-id:cross$select-list +# PASS_FOR_OUTPUT "^cross .+$") + +# select-invert +# Failed because of https://gitlab.com/inkscape/inkscape/-/issues/1565 +# add_cli_test(actions-select-invert INPUT_FILENAME path.svg PARAMETERS --actions=select-by-selector:path:nth-of-type\(2n\)$select-invert:all$select-list +# PASS_FOR_OUTPUT "^heart .+\nsquare .+\narc-big .+\ngates .+\nclone .+$") + +# select-list +# Failed because of https://gitlab.com/inkscape/inkscape/-/issues/1565 +# add_cli_test(actions-select-list INPUT_FILENAME path.svg PARAMETERS --actions=select-by-selector:*[stroke-width]:nth-child\(3n+3\)$select-list +# PASS_FOR_OUTPUT "^gates cloned: false ref: 1 href: 0 total href: 0\n$") + +# system-data-directory: test = system-data-directory +add_cli_test(actions-system-data-directory PARAMETERS --actions=system-data-directory ENVIRONMENT unset PASS_FOR_OUTPUT "inkscape\n$") + +# transform-remove +add_cli_test(actions-transform-remove INPUT_FILENAME pyramids.svg + PARAMETERS --actions=select-by-id:quad_snapped_4$transform-rotate:2$transform-remove + OUTPUT_FILENAME actions-transform-remove.png + REFERENCE_FILENAME actions-transform-remove_expected.png) + +# transform-rotate +add_cli_test(actions-transform-rotate INPUT_FILENAME pyramids.svg + PARAMETERS --actions=select-by-id:rect_snapped_4$transform-rotate:45 + OUTPUT_FILENAME actions-transform-rotate.png + REFERENCE_FILENAME actions-transform-rotate_expected.png) + +# transform-scale +add_cli_test(actions-transform-grow INPUT_FILENAME pyramids.svg + PARAMETERS --actions=select-by-id:quad_snapped_1$transform-grow:10 + OUTPUT_FILENAME actions-transform-grow.png + REFERENCE_FILENAME actions-transform-grow_expected.png) + +# transform-translate +add_cli_test(actions-transform-translate INPUT_FILENAME pyramids.svg + PARAMETERS --actions=select-by-id:quad_snapped_1$transform-translate:-5,-5 + OUTPUT_FILENAME actions-transform-translate.png + REFERENCE_FILENAME actions-transform-translate_expected.png) + +# unselect: it is deprecated +# unselect-by-id +# Failed because of https://gitlab.com/inkscape/inkscape/-/issues/1565 +# add_cli_test(actions-unselect-by-id INPUT_FILENAME path.svg PARAMETERS --actions=select-by-element:path$unselect-by-id:heart,bottom-left-corner,square,top-right-corner,arc-big,arc-small,gates$select-list +# PASS_FOR_OUTPUT "^cross .+$") + +# user-data-directory: test = user-data-directory +add_cli_test(actions-user-data-directory PARAMETERS --actions=user-data-directory ENVIRONMENT unset PASS_FOR_OUTPUT "inkscape\n$") + +# vacuum-defs +# window-close +# window-open + +# tests x/y values are not swapped in tspans. +add_cli_test(action_test_multiline_anchoring + INPUT_FILENAME multiline-anchoring.svg + PARAMETERS --actions=select-by-id:grouped_text$transform-translate:10,0$transform-translate:-10,0$export-filename:multiline-anchoring_out.svg$export-plain-svg$export-do + EXPECTED_FILES multiline-anchoring_out.svg + TEST_SCRIPT match_regex_fail.sh multiline-anchoring_out.svg "(x=\"[3-9][0-9]{2}\")|(x=\"[12][0-9]{3}\")|(y=\"[0-9]{2}\")|(y=\"1[0-9]{2}\")") + + +# The export area type should be the last set value +add_cli_test(actions-export-area-drawing-then-page + INPUT_FILENAME areas.svg + PARAMETERS --actions=export-area-drawing:true$export-area-page:true + OUTPUT_FILENAME actions-export-area-drawing-then-page.png + REFERENCE_FILENAME export-area-page_expected.png) +add_cli_test(actions-export-area-page-then-drawing + INPUT_FILENAME areas.svg + PARAMETERS --actions=export-area-page:true$export-area-drawing:true + OUTPUT_FILENAME actions-export-area-page-then-drawing.png + REFERENCE_FILENAME export-area-drawing_expected.png) + +# Sequential export-id-only action should succeed +# https://gitlab.com/inkscape/inkscape/-/issues/1957 +add_cli_test(actions-sequential-export-id-only_svg + INPUT_FILENAME theta.svg + PARAMETERS --actions=export-id-only$export-filename:actions-sequential-export-id-only.svg$export-id:red$export-do$export-filename:actions-sequential-export-id-only.svg$export-id:blue$export-do + OUTPUT_FILENAME actions-sequential-export-id-only.svg + REFERENCE_FILENAME export-id_export-id-only_expected.svg) + + +########################### +### pdf input support ### +########################### + +function(add_pdfinput_test name pages) + + add_cli_test(pdfinput-${name} + INPUT_FILENAME pdfinput/${name}.pdf + PARAMETERS --pages=${pages} --pdf-font-strategy=draw-all --actions=export-filename:${name}.svg$export-do + TEST_SCRIPT compare.sh ${name}.svg "${CMAKE_CURRENT_SOURCE_DIR}/testcases/pdfinput/${name}_expected.svg") + +endfunction(add_pdfinput_test) + +# CMS testing disabled because CI builder doesn't support LCMS yet. +# add_pdfinput_test(cms 1) +add_pdfinput_test(font-spacing 1) +add_pdfinput_test(font-style 1) +add_pdfinput_test(gradients 1) +add_pdfinput_test(latex 1) +add_pdfinput_test(transparency-groups 1) +add_pdfinput_test(rotated-gradients 1) +add_pdfinput_test(multi-page-sample all) + +########################### +### file format support ### +########################### + +# librevenge formats +if(WITH_LIBCDR) + # add_cli_test(import_cdr PARAMETERS --export-type=png # fails to open (regression in libcdr 1.6.0) + # INPUT_FILENAME librevenge_formats/corel_draw.cdr OUTPUT_FILENAME format_corel_draw.png + # FUZZYREF_FILENAME librevenge_formats/corel_draw_expected.png # check png size is correct + # FUZZ_PERCENTAGE 3) + add_cli_test(import_cdr2 PARAMETERS --export-type=png + INPUT_FILENAME librevenge_formats/corel_draw2.cdr OUTPUT_FILENAME format_corel_draw2.png + FUZZYREF_FILENAME librevenge_formats/corel_draw2_expected.png + FUZZ_PERCENTAGE 3) +endif() +if(WITH_LIBVISIO) + add_cli_test(import_vsd PARAMETERS --export-type=png + INPUT_FILENAME librevenge_formats/visio.vsd OUTPUT_FILENAME format_visio.vsd.png + FUZZYREF_FILENAME librevenge_formats/visio.vsd_expected.png + FUZZ_PERCENTAGE 3) + add_cli_test(import_vsdx PARAMETERS --export-type=png + INPUT_FILENAME librevenge_formats/visio.vsdx OUTPUT_FILENAME format_visio.vsdx.png + FUZZYREF_FILENAME librevenge_formats/visio.vsdx_expected.png + FUZZ_PERCENTAGE 3) +endif() +if(WITH_LIBWPG) + add_cli_test(import_wpg PARAMETERS --export-type=png + INPUT_FILENAME librevenge_formats/word_perfect.wpg OUTPUT_FILENAME format_word_perfect.png + FUZZYREF_FILENAME librevenge_formats/word_perfect_expected.png + FUZZ_PERCENTAGE 3) +endif() + + + +############################## +### advanced functionality ### +############################## + +# check whether INKSCAPE_DATADIR / INKSCAPE_PROFILE_DIR environment variables work +# TODO: INKSCAPE_PROFILE_DIR does not seem to be sanitized at all (i.e. is used verbatim by Inkscape) +set(fancy_dir "i_certainly_do_not_exist") +file(TO_NATIVE_PATH "${fancy_dir}/inkscape" expected_dir) +string(REPLACE "\\" "\\\\" expected_dir "${expected_dir}") +add_cli_test(inkscape_datadir PARAMETERS --system-data-directory + ENVIRONMENT INKSCAPE_DATADIR=${fancy_dir} + PASS_FOR_OUTPUT "${expected_dir}\n$") +add_cli_test(inkscape_profile_dir PARAMETERS --user-data-directory + ENVIRONMENT INKSCAPE_PROFILE_DIR=${fancy_dir}/inkscape + PASS_FOR_OUTPUT "${fancy_dir}/inkscape\n$") +add_cli_test(inkscape_profile_dir_handle_illegal + ENVIRONMENT INKSCAPE_PROFILE_DIR=invalid:dir + INPUT_FILENAME empty.svg OUTPUT_FILENAME empty.svg) + +# check if "systemLanguage" attribute is properly handled +add_cli_test(systemLanguage_en ENVIRONMENT LANGUAGE=en INPUT_FILENAME systemLanguage.svg + OUTPUT_FILENAME systemLanguage_en.png + REFERENCE_FILENAME systemLanguage_en.png) +add_cli_test(systemLanguage_fr ENVIRONMENT LANGUAGE=fr_FR INPUT_FILENAME systemLanguage.svg + OUTPUT_FILENAME systemLanguage_fr.png + REFERENCE_FILENAME systemLanguage_fr.png) +add_cli_test(systemLanguage_fr2 ENVIRONMENT LANGUAGE=fr_FR.UTF-8 INPUT_FILENAME systemLanguage.svg + OUTPUT_FILENAME systemLanguage_fr2.png + REFERENCE_FILENAME systemLanguage_fr.png) +add_cli_test(systemLanguage_de ENVIRONMENT LANGUAGE=de INPUT_FILENAME systemLanguage.svg + OUTPUT_FILENAME systemLanguage_de.png + REFERENCE_FILENAME systemLanguage_de.png) +add_cli_test(systemLanguage_de-CH ENVIRONMENT LANGUAGE=de_CH INPUT_FILENAME systemLanguage.svg + OUTPUT_FILENAME systemLanguage_de-CH.png + REFERENCE_FILENAME systemLanguage_de.png) +add_cli_test(systemLanguage_pt ENVIRONMENT LANGUAGE=pt INPUT_FILENAME systemLanguage.svg + OUTPUT_FILENAME systemLanguage_pt.png + REFERENCE_FILENAME systemLanguage_pt.png) +add_cli_test(systemLanguage_xy ENVIRONMENT LANGUAGE=xy INPUT_FILENAME systemLanguage.svg + OUTPUT_FILENAME systemLanguage_xy.png + REFERENCE_FILENAME systemLanguage_default.png) +add_cli_test(systemLanguage_fr_RDF ENVIRONMENT LANGUAGE=xy INPUT_FILENAME systemLanguage_RDF.svg + OUTPUT_FILENAME systemLanguage_fr_RDF.png + REFERENCE_FILENAME systemLanguage_fr.png) diff --git a/testfiles/cli_tests/check_output.sh b/testfiles/cli_tests/check_output.sh new file mode 100644 index 0000000..67ef176 --- /dev/null +++ b/testfiles/cli_tests/check_output.sh @@ -0,0 +1,86 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0-or-later + +command -v convert >/dev/null 2>&1 || { echo >&2 "I require ImageMagick's 'convert' but it's not installed. Aborting."; exit 1; } +command -v compare >/dev/null 2>&1 || { echo >&2 "I require ImageMagick's 'compare' but it's not installed. Aborting."; exit 1; } + +OUTPUT_FILENAME=$1 +OUTPUT_PAGE=$2 +REFERENCE_FILENAME=$3 +EXPECTED_FILES=$4 +TEST_SCRIPT=$5 + +# check if expected files exist +for file in ${EXPECTED_FILES}; do + test -f "${file}" || { echo "Error: Expected file '${file}' not found."; exit 1; } +done + +# if reference file is given check if input files exist and continue with comparison +if [ -n "${REFERENCE_FILENAME}" ]; then + if [ ! -f "${OUTPUT_FILENAME}" ]; then + echo "Error: Test file '${OUTPUT_FILENAME}' not found." + exit 1 + fi + if [ ! -f "${REFERENCE_FILENAME}" ]; then + echo "Error: Reference file '${REFERENCE_FILENAME}' not found." + exit 1 + fi + + # convert testfile and reference file to PNG format + # - use internal MSVG delegate in SVG conversions for reproducibility reasons (avoid inkscape or rsvg delegates) + [ "${OUTPUT_FILENAME##*.}" = "svg" ] && delegate1=MSVG: + [ "${REFERENCE_FILENAME##*.}" = "svg" ] && delegate2=MSVG: + + # extract a page from multipage PDF if requested and convert it to RGB + OUTFILE_SUFFIX="" + if [ -n "$OUTPUT_PAGE" ]; then + OUTFILE_SUFFIX="[${OUTPUT_PAGE}] -colorspace RGB" + fi + + if ! convert ${delegate1}${OUTPUT_FILENAME}${OUTFILE_SUFFIX} ${OUTPUT_FILENAME}.png; then + echo "Warning: Failed to convert test file '${OUTPUT_FILENAME}' to PNG format. Skipping comparison test." + exit 42 + fi + if ! convert ${delegate2}${REFERENCE_FILENAME} ${OUTPUT_FILENAME}_reference.png; then + echo "Warning: Failed to convert reference file '${REFERENCE_FILENAME}' to PNG format. Skipping comparison test." + exit 42 + fi + + # compare files + if ! compare -metric AE ${OUTPUT_FILENAME}.png ${OUTPUT_FILENAME}_reference.png ${OUTPUT_FILENAME}_compare.png; then + echo && echo "Error: Comparison failed." + exit 1 + fi +fi + +# if additional test file is specified, check existence and execute the command +if [ -n "${TEST_SCRIPT}" ]; then + script=${TEST_SCRIPT%%;*} + arguments=${TEST_SCRIPT#*;} + IFS_OLD=$IFS IFS=';' arguments_array=($arguments) IFS=$IFS_OLD + + if [ ! -f "${script}" ]; then + echo "Error: Additional test script file '${script}' not found." + exit 1 + fi + + case ${script} in + *.py) + interpreter=python3 + ;; + *) + interpreter=bash + ;; + esac + + if ! $interpreter ${script} "${arguments_array[@]}"; then + echo "Error: Additional test script failed." + echo "Full call: $interpreter ${script} $(printf "\"%s\" " "${arguments_array[@]}")" + exit 1 + fi +fi + +# cleanup +for file in ${OUTPUT_FILENAME}{,.png,_reference.png,_compare.png} ${EXPECTED_FILES}; do + rm -f ${file} +done diff --git a/testfiles/cli_tests/compare.sh b/testfiles/cli_tests/compare.sh new file mode 100644 index 0000000..e928da3 --- /dev/null +++ b/testfiles/cli_tests/compare.sh @@ -0,0 +1,13 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0-or-later + +file1=$1 +file2=$2 + +test -f "${file1}" || { echo "compare.sh: First file '${file1}' not found."; exit 1; } +test -f "${file2}" || { echo "compare.sh: Second file '${file2}' not found."; exit 1; } + +if ! cmp "${file1}" "${file2}"; then + echo "compare.sh: Files '${file1}' and '${file2}' are not identical'." + exit 1 +fi diff --git a/testfiles/cli_tests/identify.sh b/testfiles/cli_tests/identify.sh new file mode 100644 index 0000000..57bacb7 --- /dev/null +++ b/testfiles/cli_tests/identify.sh @@ -0,0 +1,11 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0-or-later + +file1=$1 +PATTERN='$2' + +test -f "${file1}" || { echo "identify.sh: File '${file1}' not found."; exit 1; } +if ! $(identify "${file1}" | grep -q -e ${PATTERN} - ); then + echo "expected $2 but got" `identify "${file1}"` + exit 1 +fi diff --git a/testfiles/cli_tests/l2compare.sh b/testfiles/cli_tests/l2compare.sh new file mode 100755 index 0000000..cbb5540 --- /dev/null +++ b/testfiles/cli_tests/l2compare.sh @@ -0,0 +1,91 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Convert an image (or a single page of a PDF/PostScript document) to a bitmap +# and calculate the relative root-mean-squared (L2) distance from the reference. +# +# Authors: +# Rafael Siejakowski +# +# Copyright (C) 2022 Authors +# +# Released under GNU GPL v2+, read the file 'COPYING' for more information. +# + +MY_LOCATION=$(dirname "$0") +source "${MY_LOCATION}/../utils/functions.sh" + +ensure_command "convert" +ensure_command "compare" +ensure_command "bc" +ensure_command "cp" + +OUTPUT_FILENAME="$1" +OUTPUT_PAGE="$2" +REFERENCE_FILENAME="$3" +PERCENTAGE_DIFFERENCE_ALLOWED="$4" +DPI="$5" + +if [ ! -f "${OUTPUT_FILENAME}" ] +then + echo "Error: Test file '${OUTPUT_FILENAME}' not found." + exit 1 +fi + +if [ ! -f "${REFERENCE_FILENAME}" ] +then + echo "Error: Reference file '${REFERENCE_FILENAME}' not found." + exit 1 +fi + +# Convert the output file to the PNG format +CONVERSION_OPTIONS="-colorspace RGB" + +# Extract a page from multipage PS/PDF if requested +OUTFILE_SUFFIX="" +if [[ "x$OUTPUT_PAGE" != "x" ]] +then + OUTFILE_SUFFIX="[${OUTPUT_PAGE}]" # Use ImageMagick's bracket operator +fi + +DPI_OPTION="" +if [[ "x$DPI" != "x" ]] +then + DPI_OPTION="-density $DPI" +fi + +if [[ $(identify -format "%m" "${OUTPUT_FILENAME}") != "PNG" ]] +then + if ! convert $DPI_OPTION "${OUTPUT_FILENAME}${OUTFILE_SUFFIX}" $CONVERSION_OPTIONS "${OUTPUT_FILENAME}-output.png" + then + echo "Warning: Failed to convert test file '${OUTPUT_FILENAME}' to PNG format. Skipping comparison test." + exit 42 + fi +else + cp "${OUTPUT_FILENAME}" "${OUTPUT_FILENAME}-output.png" +fi + + +# Copy the reference file +cp "${REFERENCE_FILENAME}" "${OUTPUT_FILENAME}-reference.png" + +# Compare the two files +COMPARE_OUTPUT=$(compare 2>&1 -metric RMSE "${OUTPUT_FILENAME}-output.png" "${OUTPUT_FILENAME}-reference.png" \ + "${OUTPUT_FILENAME}-diff.png") +RELATIVE_ERROR=$(get_compare_result "$COMPARE_OUTPUT") +PERCENTAGE_ERROR=$(fraction_to_percentage "$RELATIVE_ERROR") + +if (( $(is_relative_error_within_tolerance "$RELATIVE_ERROR" "$PERCENTAGE_DIFFERENCE_ALLOWED") )) +then + # Test passed: print stats and clean up the files. + echo "Fuzzy comparison PASSED; error of ${PERCENTAGE_ERROR}% is within ${PERCENTAGE_DIFFERENCE_ALLOWED}% tolerance." + for FILE in ${OUTPUT_FILENAME}{,-reference.png,-output.png,-diff.png} + do + rm -f "${FILE}" + done +else + # Test failed! + echo "Fuzzy comparison FAILED; error of ${PERCENTAGE_ERROR}% exceeds ${PERCENTAGE_DIFFERENCE_ALLOWED}% tolerance." + exit 1 +fi + diff --git a/testfiles/cli_tests/match_regex.sh b/testfiles/cli_tests/match_regex.sh new file mode 100644 index 0000000..24a6347 --- /dev/null +++ b/testfiles/cli_tests/match_regex.sh @@ -0,0 +1,13 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0-or-later + +testfile=$1 +regex=$2 + +test -f "${testfile}" || { echo "match_regex.sh: testfile '${testfile}' not found."; exit 1; } +test -n "${regex}" || { echo "match_regex.sh: no regex to match spoecified."; exit 1; } + +if ! grep -E "${regex}" "${testfile}"; then + echo "match_regex.sh: regex '${regex}' does not match in testfile '${testfile}'." + exit 1 +fi diff --git a/testfiles/cli_tests/match_regex_fail.sh b/testfiles/cli_tests/match_regex_fail.sh new file mode 100644 index 0000000..b6abca8 --- /dev/null +++ b/testfiles/cli_tests/match_regex_fail.sh @@ -0,0 +1,13 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0-or-later + +testfile=$1 +regex=$2 + +test -f "${testfile}" || { echo "match_regex.sh: testfile '${testfile}' not found."; exit 1; } +test -n "${regex}" || { echo "match_regex.sh: no regex to match spoecified."; exit 1; } + +if grep -E "${regex}" "${testfile}"; then + echo "match_regex.sh: regex '${regex}' matches in testfile '${testfile}'." + exit 1 +fi diff --git a/testfiles/cli_tests/test_no_output_file.sh b/testfiles/cli_tests/test_no_output_file.sh new file mode 100644 index 0000000..02f9455 --- /dev/null +++ b/testfiles/cli_tests/test_no_output_file.sh @@ -0,0 +1,10 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0-or-later + +testfile=$1 + +if test -f "${testfile}" +then + echo "test_no_output_file.sh: testfile '${testfile}' was found."; + exit 1; +fi diff --git a/testfiles/cli_tests/testcases/actions-file-open_expected.png b/testfiles/cli_tests/testcases/actions-file-open_expected.png new file mode 100644 index 0000000..c030bb6 Binary files /dev/null and b/testfiles/cli_tests/testcases/actions-file-open_expected.png differ diff --git a/testfiles/cli_tests/testcases/actions-object-align_expected.png b/testfiles/cli_tests/testcases/actions-object-align_expected.png new file mode 100644 index 0000000..e836df8 Binary files /dev/null and b/testfiles/cli_tests/testcases/actions-object-align_expected.png differ diff --git a/testfiles/cli_tests/testcases/actions-object-distribute_expected.png b/testfiles/cli_tests/testcases/actions-object-distribute_expected.png new file mode 100644 index 0000000..c20de15 Binary files /dev/null and b/testfiles/cli_tests/testcases/actions-object-distribute_expected.png differ diff --git a/testfiles/cli_tests/testcases/actions-object-set-attribute_expected.png b/testfiles/cli_tests/testcases/actions-object-set-attribute_expected.png new file mode 100644 index 0000000..c4ce8d3 Binary files /dev/null and b/testfiles/cli_tests/testcases/actions-object-set-attribute_expected.png differ diff --git a/testfiles/cli_tests/testcases/actions-object-simplify-path_expected.png b/testfiles/cli_tests/testcases/actions-object-simplify-path_expected.png new file mode 100644 index 0000000..4b0f4e2 Binary files /dev/null and b/testfiles/cli_tests/testcases/actions-object-simplify-path_expected.png differ diff --git a/testfiles/cli_tests/testcases/actions-object-stroke-to-path_expected.png b/testfiles/cli_tests/testcases/actions-object-stroke-to-path_expected.png new file mode 100644 index 0000000..23ebe57 Binary files /dev/null and b/testfiles/cli_tests/testcases/actions-object-stroke-to-path_expected.png differ diff --git a/testfiles/cli_tests/testcases/actions-object-unlink-clones_expected.png b/testfiles/cli_tests/testcases/actions-object-unlink-clones_expected.png new file mode 100644 index 0000000..9ad52b3 Binary files /dev/null and b/testfiles/cli_tests/testcases/actions-object-unlink-clones_expected.png differ diff --git a/testfiles/cli_tests/testcases/actions-open-page_expected.png b/testfiles/cli_tests/testcases/actions-open-page_expected.png new file mode 100644 index 0000000..abb0fcc Binary files /dev/null and b/testfiles/cli_tests/testcases/actions-open-page_expected.png differ diff --git a/testfiles/cli_tests/testcases/actions-transform-grow_expected.png b/testfiles/cli_tests/testcases/actions-transform-grow_expected.png new file mode 100644 index 0000000..4bcf5aa Binary files /dev/null and b/testfiles/cli_tests/testcases/actions-transform-grow_expected.png differ diff --git a/testfiles/cli_tests/testcases/actions-transform-remove_expected.png b/testfiles/cli_tests/testcases/actions-transform-remove_expected.png new file mode 100644 index 0000000..f0cbc56 Binary files /dev/null and b/testfiles/cli_tests/testcases/actions-transform-remove_expected.png differ diff --git a/testfiles/cli_tests/testcases/actions-transform-rotate_expected.png b/testfiles/cli_tests/testcases/actions-transform-rotate_expected.png new file mode 100644 index 0000000..ce66cc6 Binary files /dev/null and b/testfiles/cli_tests/testcases/actions-transform-rotate_expected.png differ diff --git a/testfiles/cli_tests/testcases/actions-transform-translate_expected.png b/testfiles/cli_tests/testcases/actions-transform-translate_expected.png new file mode 100644 index 0000000..ef3c5ad Binary files /dev/null and b/testfiles/cli_tests/testcases/actions-transform-translate_expected.png differ diff --git a/testfiles/cli_tests/testcases/areas.svg b/testfiles/cli_tests/testcases/areas.svg new file mode 100644 index 0000000..b07a5b5 --- /dev/null +++ b/testfiles/cli_tests/testcases/areas.svg @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/testfiles/cli_tests/testcases/empty.svg b/testfiles/cli_tests/testcases/empty.svg new file mode 100644 index 0000000..b1010c3 --- /dev/null +++ b/testfiles/cli_tests/testcases/empty.svg @@ -0,0 +1,3 @@ + + + diff --git a/testfiles/cli_tests/testcases/export-area-drawing_expected.emf b/testfiles/cli_tests/testcases/export-area-drawing_expected.emf new file mode 100644 index 0000000..5f64188 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-area-drawing_expected.emf differ diff --git a/testfiles/cli_tests/testcases/export-area-drawing_expected.eps b/testfiles/cli_tests/testcases/export-area-drawing_expected.eps new file mode 100644 index 0000000..f225577 --- /dev/null +++ b/testfiles/cli_tests/testcases/export-area-drawing_expected.eps @@ -0,0 +1,443 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: cairo 1.16.0 (https://cairographics.org) +%%CreationDate: Thu Feb 27 23:52:53 2020 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 3 +%%BoundingBox: 0 0 248 206 +%%EndComments +%%BeginProlog +50 dict begin +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageBoundingBox: 0 0 248 206 +%%EndPageSetup +q 0 0 248 206 rectclip +1 0 0 -1 0 206 cm q +0.254902 0.411765 0.882353 rg +14.172 0 m 127.559 0 l 135.41 0 141.73 6.32 141.73 14.172 c 141.73 70.867 + l 141.73 78.719 135.41 85.039 127.559 85.039 c 14.172 85.039 l 6.32 85.039 + 0 78.719 0 70.867 c 0 14.172 l 0 6.32 6.32 0 14.172 0 c h +14.172 0 m f +1 0 0 rg +243.668 152.23 m 184.707 148.898 l 148.539 195.559 l 133.488 138.473 l +77.918 118.504 l 127.578 86.559 l 129.402 27.555 l 175.141 64.895 l 231.84 + 48.395 l 210.449 103.418 l h +243.668 152.23 m f +0.501961 0 0.501961 rg +4.251969 w +0 J +0 j +[] 0.0 d +4 M q 1 0 0 1 0 0 cm +243.668 152.23 m 184.707 148.898 l 148.539 195.559 l 133.488 138.473 l +77.918 118.504 l 127.578 86.559 l 129.402 27.555 l 175.141 64.895 l 231.84 + 48.395 l 210.449 103.418 l h +243.668 152.23 m S Q +0 0.501961 0 rg +116.219 153.07 m 116.219 181.25 93.375 204.094 65.195 204.094 c 37.016 +204.094 14.172 181.25 14.172 153.07 c 14.172 124.891 37.016 102.047 65.195 + 102.047 c 93.375 102.047 116.219 124.891 116.219 153.07 c h +116.219 153.07 m f +0 g +2.834646 w +q 1 0 0 1 0 0 cm +116.219 153.07 m 116.219 181.25 93.375 204.094 65.195 204.094 c 37.016 +204.094 14.172 181.25 14.172 153.07 c 14.172 124.891 37.016 102.047 65.195 + 102.047 c 93.375 102.047 116.219 124.891 116.219 153.07 c h +116.219 153.07 m S Q +Q q +39 17 151 151 re W n +q +39 17 151 151 re W n +% Fallback Image: x=39 y=17 w=151 h=151 res=300ppi size=1190700 +[ 151.2 0 0 -151.2 39 168.2 ] concat +/cairo_ascii85_file currentfile /ASCII85Decode filter def +/DeviceRGB setcolorspace +<< + /ImageType 1 + /Width 630 + /Height 630 + /Interpolate false + /BitsPerComponent 8 + /Decode [ 0 1 0 1 0 1 ] + /DataSource cairo_ascii85_file /FlateDecode filter + /ImageMatrix [ 630 0 0 -630 0 630 ] +>> +cairo_image + Gb"0WGFVoNIH^]DMs@>:hKuG,0.X(r,f,]L#qUH]rg8Q\:I]joW(", + UT5GOfW;#Y*)i!>1tE8h,\O.4@G&jg$c)%$beRNlaq3MXV8gH@>\(UFekStW@gX? + $.>Co=LtPM<#'4hP%tkD9FtKkDeb!,IV_T%-_@a8<&7bW2mqr'uX$_SS)C(4\9_G0atVltr + 63+5F1fr249KKnX.f/-'IGmJ?4e7NHbiNqa.9"ptaa:fm_K*.NCV]nLU!fK!g;d=C*N6t,$ + l$4FY%u=\1nfi>Dt1^ + m\2+-c]BBi2i`IYj`'T"^oUP>:.?sfTqd:L\ArYrL#)c4@H":H/kFSG,bE^)@6Kk + Deb!,G>JSmYCI?`g+`OtH5\;cn:!_?-t(bbjoBKtY0_@`tR;K*!PP-nIJJ@0'J?SC\OS2iU + &+DkYKA1PLNq-nG?[p)IAsZEPp.?:dFki>Dt1^lH)uZE4i7N5$kl-nIJJ@0,#cp[E3-Klrp + @k0lp0$4G0V=9*O241*4@@F%>C;XE;Uf]sWK_SS)C(4[Dc#;$bc[S>cm^$"cT$470e:fm^@ + j;_9:^T^SUG0f_s,mB!VB.,;248/,Mi_a,)-['aBP(LQf1DCUT*W'mnHWSiU^KO46eG>5#@ + Gr(2$c%c)q\FH\fJC!<;G9'SfCDN@$4G0V=9*M(mIRon/WRJ$a5W.S@Gr(2$c%dTHPZBT=N + Bs`Xf\ClYj`'T"^oBEpZD?R=N@CXPP6*'n>$$k$dVXO!-T?.GFkF\Fe%<)@3l)2KkDeb!': + \(qiK-X]ku5CVsJ;MC^LQr:fm^@*.DP'5[gGCXJDMnn-k!S@)nD=k"$6$/iB>:/JP=H_m/9juuu$4FY0UMnGD/aSnh8rA6-Y,%_pn-k!S@)oP.ZOE:I;%Knf + fctF]NW7%rYj`'T"^oBU2eEuXYYf%>f3S"?(qYnn^2nUj.k)kt4@G?o*NU5oS'DCV-=@keei>Dt1^lB'iV\ + qnleuoJIE,&U[qq6tqR8)]P^[k8@5;n4LB.,;24&5.0RV;\@p"7j.[]7d3Sc?CZc5t_'F3h + ,gIk46$i>Dt1^lFW5V^*1p4?_i`_E)FSVgLY]DIairP(LQf1DCUT*N-=l>Z`AKpFWJA;.g[ + /@Dt1^lJ.rS![mRcKX:B$gXbB=k6YPSCfi=Dfd9/K + nX.f/-%b,Dk<&(k)/f8GREce8rBa[=&MjsG_X':YiGAQnVK0,=:^CtYhlLX\6Q=EH4D0&;- + 3gA(^/,5B)I@"=\3U9pTY[jgeHh4jtDl=`CNT8(4ZR8Nik(7>H&[6:gq%A8TE_9m,fYo\eo + Tf=9'-'2(Iqr4ij;#/Lr7WCp0W_IV3t*h)aegN+>7`!EcT#8@Ch60'WILL%T+*7DY!U2_.s + %O(BRG$4FY$I>=+-'Wk('2_Jpia0O8LA]Mq6j@&PjFsgeA0,1E.c"/YTW,E&qc'AkWW#Sl + _rG&cfu*c,?qZnErD!JdSh(C7:?4!Y^c[^K7mOX]keG7dB(eiojGCoc.b"I\h%'3:a;ba/- + $(Ok:ACPf2^YU@UZ$68V,fmYQ-46Y]$4$]IJ%7gP*?.]'a<_-,i/N;BSgN(4ZPnpF\/@A/3bSVFRRH9#4?n+)^m6;$]_k_f-!u!\s#@4FYr<*p/b/)5.UYR. + k0s1'Gc3&8)B\E:oKkpUMkk"'jXCN?4X=\ft8mMDHpF!4in:0'Gc3&8)KhROJb.q_9^!,W6 + i6F7b1FcC7b&O7fZCg!#?R14j+sA%D'7QKtS4,8Wh%c$@%TJ!jLV8)`%r'e>P/l$@=;7P+k + d6Zl@\>Dbb/J@3l(\;-3f6*8. + jSM"$s>LA]MA_'p\C`6Z+G-U[U7hAL!!#?R33R&[9:>fXjgEO4l_XbaQXJ>iM"^o<+*@?IT + DelCWh9#LcMW/iX2qG*r*qBk1@0'JLEr4?S"-f_(LkkXu%D(@UlROE-:;0l=;L6VU$AAXk_ + 9$Om'!749bpZoK]3RRI5kY^>2]rrrtQef`SM6o')CLSH;FHtapF3XM=9*7+3OZdI0r93#GWOmJd7T:n\olF9J9NCm + !4VYA2(@m;4\35T$#.nW7=(?Wa;4-0<*-J"3X"pXKa$DchBU\XEOi"QMFTI"&J%o?jDH#g] + l]Za$fD0q$c&mXLR/sEV+_C20;]Uf;BS.;(4]Wd%P9JMasGk>]TpL:FdD]c'W(\=ZB?EB:f + m^8jW%N?En.&aMMKUh031sbm[@C1A$pCp?jhYli)\-S,mH6;XS[fTdNpPI;WM\c^e0Jp6"' + f([=A3cK9T@!hVsB60;?pc?PVosQB`0+K#.S.COaVRK8s3a]u9e]M\dlm/f^>9gr.7=TgiG + k\922O8RX,l:[p2DjN1#OG?@EKC(qG6>6jmT1rtGnh'\d_]0'7gEO0\^F=CUP_?+]Bi1:)V + mC9%*M)WhnLebR\#M>#PmcO&k8sW)i!X:&(!4NHO2r>_tf6(0b+F:tiPnYV.h27"9!st[*! + 8KX-mQ?,4gsB?F;s55T<`51KS^Gs&a0HjT@0*l\pmm0KH;UHD`&m1^HJSS%*aq"5XJ?+:$4 + FYO,B(mcLdeH6/07*\21)Tk@`sGiZ>AcK+A>!I_dVMNgPbo]l#JE5!M'Zlih^"h8gAF26EQqNH@\[+;>T + A>K:p51oNcoDC8aRT_W=\02j'Gc4'a'YHId"Q)&0&AZ= + d3f+MN3&f@kZh.d":Ttfi7@GMV]->d\XF0bSY0_gQ#D!q7HrZsbmiJ*'H@f&J6OCE:,BtBG + iQ_"k]YNY;OlQ#V>`M+.0>+Jc(Gd%eRl(+@dWf0!)3,T@'u^o^O>-RU + tQ$h['\W!,ndmSISD-,mDRV`*,/Pn$O2U9FXQH@O,V6mr";4[Qep!S!o$MY0fXOj2cAZ*rY + #l.2XW$4FYOe#t'&fQ2AKGg:miU=77I=@l@iZpZr6.>YhBE9m/Y$&Qt*,!AW4AO.);8R_F) + =]SEs=9,%9B%-NCVgYfk.UKB1X3mL0ok:t"/_MqTWICC[G>\[""lP]_W%Z\R`=j#7?EBqNg + c>1B'i,10YQ.!tcdFCkEm\5n=Zj=,:,/^$J5>;1 + <7d,dK_I6-5,'!cV`>iT1:(rLt>USulsgY,&<"Uk&%sZ^Mh98O6bc"Ot.Qb7&!0RI2`bXO) + g,U_mf)ZKL)7g/,+F\jXYt_?+[&kFbCTbB71^@"hIAc7%(-lp`m_gVfQ(/#6[>PUJ'_=)nd + %243ZqFW^-WKiCIpoF4KUjJ>R-nG@"p?u?t0[_InDt-e\RR\kW,0ep?6t$*! + `VuCH#/g@rmNipRYcHZIPE^I-=@mN+N@FNZ<_K + >?UaRboK$4FWs + 8)KhR0W=;[Hgq*4!fpA[goOa0*Nr;4YQ.og[X4=VP]"k8`9WU2,!,+c*agq4\XUWH.p4@XY + Mc1[!*hPX`_;!sm9EkPV^Jr)nDF-Fl\O%u_F@O4IbhAK#sHn + 7p!!I3W!ldH:iF$up_(FKC:lh4ua#eQ3NgbkLb'GM=5(Ja=eAh]Nh;-5!5XJFK(Tt2dZ3bJ + I]@0*lY@`Qtd#BHsg.it//`_$RF$QoRIDP**]-'')lSJ\:(XWJ^u%9) + \G4>Y.4i!OuSmE_H$9nW_rtRWWqU*c*d@[oZI48thLa9b$EkT3nH[ql^\LJ?_7-=.&p1TcP + cA&[][Z8d\)-R0DU;o2J*%R``Ld-$s7bDSH&c4+Gr^b7k_BZ+WV\]q>U%(Bh!Kf3RuaYGA[ + IE-@DHUi?cFl6GZ$*6o)-q88 + =0%4ES3J\XDp&7OoV8Vjgt6ga\\X4Ve,@8?JN,OON++s"DIi@J9rtH&iC]!K`nDpRQ_0^jm + ]F!BEDos>DX9Hlb[A4mdf%,%JbVZU>+#UG.@b0=q.RdAZ#8s:P,3#&Y#8@%f&[lVC+rr:F^ + OZR+Al.M]CdNT"El([,dO,HC=Ul47E+_a4T:_b!cTgU^_2:A;ka)EpAtP6Ff0UMg2:GlKK0 + j*i^n"7gp$dtL#l)($aqFq$^AL0Y\X:<`h)!V9*&PI3rQ]4A2gf>>:/.8\D5]ZeR*gLVJp1b%u6m`!.8`nha2t; + ;lP,"B*'\kdClV/WVk8Y87[@N@K"`cCiX-q/;R"1'4HQ + 6R>_hJi@4p!gWq]AraR%>=rs":+\F2jc_ZF>r_N^l%>JMNN)"FRIDQcBY:(YVt%61]*IIK` + r"jLYeM50R91(+7V2'Q6r/"ik\4okGluVXHAKb"R+@\*c2a/N%CD0$CuB'OliOV%==V5[YUXCQ?6l + \Z%ZJ[f@I`d-]-CiHA."Y]E_8Vi?LMciI`)\Mc + 1VclM9A>*a,ZCf'(BkSbAO;!=B4MKk%`,(lY4:SKU2pi=@SB;?TCj*`oFu?I?IO*ct`iKa$ + DOn^gL,T%U3o`@!lt'4"0a6p-:6jQk20omlA>C[@BBKa$DOnX\,;B%1bt9c?o[o5FQ4mNWc + C2i@73$JkT1Q`KjL@0*lW@Y[N``JAMA,iT.Er#1j%eMct$c%Hp> + :1dZapRbhnKAEGcF)U74]&ek/XNO4q3I!l'Gc3,Vm[k`)n&S('VFk#Jh+F!86'a(fFg?cq/ + $j`C(R$)#`*5djkEB7k?#8Nh*8&\g"8W"!P!)pF2EV_d_j$/4+bA\OE> + s9nh.!jhm0M@_?+]8oVR9`G[7D+M,/6sMY35K/`3kEVRm0'2PI>T>W@rBUVr[*.R)8m4;Ls + q[!^%D5YiK!YQ.Aa3OZe4%$p1pntQ_HFBs)`Fa\pJ"'R?ZrVi+B/JJtNWI9Pf)&2e-mO(6q&-b(8(XJF7G`'?>0U6]uWB + jt9F^Osl#P`hd!\fm\@#70X0C(gmUCZhpK`tk!s8KtAKrr_8j[UEN!t#FOeG^GO_HtX'j@: + n0J(E+!r8p?UDI=MhV/-&TRpFJ!Hlo!TnIe3Y)TWeD + i*I;Y<6"-t_Y4A=un,\4H@-Z+No*btQ6[+Y7I=MhV/-&V(hg*;9YVeI7Ie3Y)TWeDi*I3"7 + 6"+POBDKp[pjg3_0QlHY3.3Kl%+=j0JcQ-nG?sa**jb[S^\[O$:>a'Gc4uj0-c_-u?4bQQLX+%k&t/;o + `7;DG\uA:6QcJJ.F%Z!)k7g:,=<"r7hDP6"'e]3k8a8U3Y#6A*)kpi"$/&^spKn;\CZu^)e + L`pAf\>=9,3nD+hCC"a[QRg]RI?@0)`V[<6MaKe:e!jl[]C%gi][eGEGs-/GtVVh`IA4hr-R(4`C5RjTIbfHNIPIdR79TWeDiS_5h`dD?[bU%378n-=XN?n0R: + 3_K'Qq7-b*K>I[DZKLEYK>O53KB22:i"$/&^rY@Wf`m/DTEiWc4hr-R(4_O\LR/qop7`TN5 + PG/-:fm]ep)R=K:tRPZ?[hDDrh-IqN8ts8.;ZQ_.mNb.Io'5I7WJti2(7g&h+)q^!WJar!0 + $:%^VleL'tUmPg]RI?@0%4uTI[DEa[cKK>LsF4ltiJi"$/&^rV)?$Om + GIWI)_44hr-R(4_O"HPRG4[Rtb1T6l)Q-nG?CLYeM=:tY*FX8L`Tr'=`l(ch6V4G1N-cs>_ + q*oIP9$c'iWG98RgCphQOT6l)Q-nG?CLYnM8:tQ]B=aoc>rh-IqN#[+YN^7)hFXR!q#P`qg + !\eZEW3CEUKDaL,r'=`l(chHTCkKUUlh9mj&+BgX"^pEDmTBaJD:%\2k8rOl$4FYjYr?85' + XgkOb0N5d*u#K@OlO&c(_jDMB?8C^^`O7+JnBbYDs5Y7"4>#^7hb2'"@&f$A63]lG<$Y + &+C$^"d$7-7YSOK#K[K>^c)rC5iVJYaYkR676NnbkQ/DkYQ,=tI\ + :'-u)&2e-mO(6k!8gUT!/\lZ\C@1UjgqYnci\Y_@0%W+kJ.^__H,kanj=tI\Gc:@)\ + i"'mNk*i!8gUT!/]I&ZcJfD=*7=7Ta1@I_?*9`JUIn8$@m$4@trLYag9luh,aIcpgHl)t$^hl\sUrsG^MT]b?6+1"KDa'uHp_@m$4Ge$eR!Orll/mi[^AOlVo]H4^c)rC^ooUSXtN!o06`WA9@m + 58'+aRGK`r$1j5TY-mB40^e$_A%7Xj=tI\O&3b%n"]GOb/Bn(R%dVh`m/D??el_?-^]r>pg + (jm'*PFK\nr!fIa\@=_BhCQCk&MY5>B[S.fT;-4qE75e0^0$\miX#laiq0G`"mOg``!'q;7 + 828]i8DL7F%q^5'5[f5n&S^+[`?'jt($JLRFBA:["p=s\"]BtsHR)d#e-"?]#lFm\"d5G.H + R'SbH=W$'/3hL35bYDI;S,f7]Q!%En(O52a-Gt>X@!-YK>I[]MEMT&0FA15UDk#8ZUY'.7U + ^l/dSR9IFRMn!04!6U!Q9]/!_FWJ7j;lp"Rk'e*XNqi'Ga4Hm#_8LLVa*9kK=p'dF[%r!@3 + Q>!b'(i#p?`+/.bSCOT9un_?(l[I4\_X8.\+,6jGZD"LQ2"jH?abdk9EnDT;,Voc88 + FU8Q7&B<#5@f;/7:?5SYt"jd#o"0EP:q8.Y3BipZNU>_"oNE4lH41[Vg<[RarUj35U\2A^# + l3bjep`U!MgP>$[@Un&jV>KY^gAT)qL>rSa5NI$aW>)#VHGlG_8-l5cUT`J8G`QCJ+O`6QLrlsel!5aZ2!XW=s"G"!Y=+:db#!0AlA$abaR&c`>;=PuKt,6>Rh$4GW"HR&U$7:Cc)`Z>EdG#6gn)B(M=] + XXu7@)[s9b75Loin1YS_2]Q-[r.c2lDq635PicdJ,"e5NdpuIBeB@S?i4Ano,hr9Z`;)V^GOFE/2qEldj.L6HjXU[]:@e&*Ps#aFW^uNo2E,P&.9ZK?gC&N-e(NXUhUFdioC: + -h1\.fkL9+b>Dr/,JUITOreh'g'4WQSZ%iWr;lI;u[QhULXh]MXHcOPHp`NfI?Io$'1J!mQ + eo0Z9kCMMFWs08@Hp.,1f/7f08p$*IYEmhM6r78>(54QRqN>qc@T%uO[/&Q6iXdktGgGqLQ + UQ5n4qWX-f:[@r&c;2aYYGZd#,B6l\@?1PQ>*?7sV55]OCmk%bDbp;p7gDhZeu`H&FQmVQ4 + _\VFaH2Etf(=CY\^CHGs33ZHA8-JjHLX5_,etH-!+&6S_E+eZEFRU^IPg)=(IgJ&Vu?L:OR + Aea-SmPqe>U[<5OZcKO)qIFSNhZr7/l(4\?JI)J;6Hqf^n9-3a$b)l&gl>okN)29fdKL38M + /WY$BZ#$1hgZL$r7_7V9I/8)hQ/rr4\oZ'1f + =](=0[B7.=Yh+4I0'WK(OT9?)+:DC[^A?A]B0Yq)YQ4i(iY,5W==:17s7r/qIs=dL_O@/t=/Ga + B2+":!8ki?$G18lO/WO#.eB4^#7'6F\s0s?Ns1@dPs7tp,s4aeN%9BL,;BbjPH.K(bhu<>f + _SR&ITet+m];B]-9M[QJ.=6F0KLj?>J-lK-*m&5mR",O86jbPjkmBE_SUJ) + :S(hbbVLJjpMTHoh!Z2qelrshHe0(0;s9:G8$Bl2+SqMp.&Hk?,BnT\>-XgJ:OY(Bq/&_dY + ;`L_(;2TMdnA:#H6@P[!$oQ^@;k"-s+;Ce?b!>fC*;V + _u]PB;#=hkn-WKi(bB*rE?8,"qp;$6XFq(\[F]e_g/A\:4C(q4ol@W^mVC3'#Wo$Yk+TESgS[:+`3A7\LL'&MWb@70HR- + U;dCjF;kmG4l=G:U0^4"fuIXDr-L2UFqQ7MW@dYU8JQ_HEN:\SD'@nUIZY%R^e,LK9crD5:mli4NNH.?&hkM0=C#n.H?=n*-LMoWd++Rfm#B(Ok4q* + WcWI\G9VRuo74^GfJ.mLm-^1[CWGZ+XIkX##NGKCkg&6tb3mBob'Gfpn`f(TgJ*f)`=m'`DA*-tVr@lIG;ahjJlTpLe9FYX\a[;MZ*sU8$ + XCs7B6XRJ+AP3[4DpOQ>''O*a$de9m`E6qs6+Fm%'G54"CkX%1\)cLUEc!3)X'qX/RBgT+C + /4+f:OH@*jLddnR[pi:J/V7hrU="ijIiu^d;e5l96!:ZJ[n^p&%W46K9B0Q7fEqDFW6^3k" + R@3d=&+hjNmpk_H27c;PKDaI55Y?$'fIkS?['JEtO"3(_=ZJXVB"[mdI8Bgi6rd797H"E$>J3\q*Yha"DN;RC3f''>Sr,`$fA5]?,NINc)"=32Z:=s]Yd@]g:=4j>d8no + jKMo^QbHl54eQpGC(,Jaj-p].=3G/=ETbrNl..#l + sDS'_\kF@Ge'gHQrV/D;2\Z8!l7P5!J\*nVo(>i!kHSi'qd7ci.fdp1(4l+P;N& + fO5g:4LcLNp6!5q`:"P.4M)Q:kTT5JE@T1cr0!2MR]M]hnc;V?fN_TU/"s5T:mo&%l60`WY + \_BScHE%XbJg%h1aWp8K@6\4KHt6AT0EQeT[XII_,VO.23Pko5aK&!L_*g4T,c-+q)15$B&Hf-'N\JSm8IfH/K\ + C.jus3fN4=VV\2-7b>sV;OFC[@8-0J@amJCdbu'=hO"=jj0`]fR/5j;fV:&=0kHSk&\eorZ + 'G`.WObCKgo@/($+gM*,;-9WSO!OYI]4ZRoqAfnc]I5=/asWEgY7`nRpo*hS'L52d=JVB]. + b-g2FM@^oddL*7@74OdJt>f=Z\ndYV5`oH.K*9N&3:L@WTbk.\uPOd'ArJYV:PX+@Ig9ggF.$6:Cl+-nGqY,: + `]aa?gX;Ds@!RJ0:Mh5Wf\Trm(Ok>l&jX+:'AH&T.AFl>j + =mr8>*-nLIYQ]rDii]O8So&Ul7sTs0&oT3&%FMW]*-;Wl_!!Mfu.$Wupb($ + BjaZ1PXLJ3PG5@*JE;U6h,`Wo?&+1Y%o^9>CE+G`'?>G!-`3%Z9/3-iN`Di$f!@=LV8KH+&/hH3+*9%YY( + fHV(3N.\8G1W+u6aoFM#\&IJnjiKGb:o63u&%flYOTs0&;VQ4r:YHFnba3o#/J/9Ubi/q[4 + ?)@>g#GgM_!&%/N^cN(-GYYkEFm=>%UB#ge'Gg2`91nkTnXL+@aGf=>q1/O]+pn^&l9d%mP + Er(pp:l?Hl2J*%PPeM_?j25f]J`*qF-b.s3hs_U$MDK`\kdrr00^NY\3XO(YC0HNF;<-tO1 + uUa'%n$hH<@$3fq^Fb#6b)0'Gg2SC_b9!1o'Z5Kl'haLKreJtLq:E + A$HJce)o(8<-H9en+_ECa]3)E_m+3RVGB@b!r%k;Cr!^3PgpZ%hn+Qt4)i/]NAeOd`$IdlM + f@RdfF=ZrKGpMS@FjN6"'edX]=n]%kR23^9Dh<'6Qb*0i(8.g) + L'9Pf:_Ph@inc]/R(/!q65E"oPi9`H*+#?r-*BD7q=^Jf`#($G]j[(@^4>ESXTaeL>qH^Q7 + n5Z7YY?BHH@>G_O!9,2uhso\:hkHrioahMM/3P@kQ=p + S@0%hI4`KS6IW9n1Kl,5C]H[2;+$QV,9MnnWb3,!];_cV*E?5!2Co`j,qQRU60L9+J6/ts- + k83$`Y^OBbQ3[99kmm8_$O@+I$OHT=(!l^.`ZF,nD8eY85eJKr+DZdjH+IUa+8J]>mrsWob + :A)[-nHM,%ob[Sn,I.hff,da=7ol^cj+e<$4FrORSm9McmQ@8;mS@mI*GY]$s/ + !/g?/.`@gWd)f"IP5aPQBeK_6 + \?B%!EIV&ZOUTd"VHpGY5)&[.T0!2E#fbR&R + 8X>Xg@)D-P-rF"TiA-MKqDbnKB*1D:Pr)6KE\]jgqYnn-!3pYWr3eSr.gip[?Ja]KnH]/aB + LBnp,N"3B^VrGQKTs=ENrC[C=cE#/jB$l0U1i]>m]IYn1;6=G + Ef>HD;*c[9?7GTts">7#FPpQV;E^k5Hc+NQaH@\,#I\r-70]s=DS%16=>#Kon?i'AbB&7f. + .]fn/&9nhS2Z#.+%I[NK1oCU[k:6Qcq^jT'^+Q3*qQtQ$QLVskj;H*NTZcD%Tl;$o&'Y#e' + $O#6)(%ZhGX(0']l(6SVV[D6aIZOX*7:Catqk/glQ7_DShFI=_:X1aECp>$.5=gA&U9HnuI + 9slTSN:?3f<5udUeB7)Z4./1X2d*`FST&Hgl'#N_:M]+4T"h((/L`!OerS"&%nso=n8m5\/ + 0i&Jo?g0H2crEVMHVJ4gRZ7Q0Z0TZP9ncKa!'HNJ`"QG[d%b+5NtWZbW6HdA:fnk8?AuRA+tG1=\TrgP,2J,m1jc%SFkp&HS-!o + .YWsW/<\[C!IcqlZj!`_Be"EpVH@YoQ"n"]cJP_0_AuD:"ClW`^h7HjpaIobkHBe=e"n"]c + JP]IO@\.LcA"7cch5e/rb3YnU#2N%'U&oP5YWsW7To+-pi"$$LpX+kMm?7qc>G\sT:K67I' + G^t.VcCqsDHrKP*3H_qBT>Ae0Bb^m',A"jK`sg%AYn+][SM3q>'9L!nNONcDXu6))[SD%.+0%RV$4DO+C_cR>gV:uIn"UVOY>7&)lkS>LI]* + =M-nGoFlVu+"c#8UYO/!GVN]goLfMXk#I]*=M-nGoFJ#$PF:ObDC[M(`?>Zbl?fK;;TU=82 + d.aWda!b(Vs.csmEZB'5MX_=_AQb1B#8Tlu&Zi"&3>#8P3-ltl/R[_mkDDq;=p4huZ5,]&_=* + ;gor7X';5`[t,7hk9_-$i+]*dgfq.+eDIFRMiXo:#)j?$EF%?#"M[HJP:bc7W$t+@I!GR-0 + 5hS08qD>>!bA@8PTL\tAc!7d)g[F@p<;HFpAngJK$jrU-;?*uY.=Zr+e)YIp#,:Y9B$-$Q' + !E-&0>`5aaPV+G;\=/iY!g?L?'Ip%56!/T*6.d$QF-\dGDd?^8'=.2jl7\(tlZk4&cSQ;hI + >0e^Q"[_@t<6V[\9jdEG=qfSV[q10-A@n_NM9g$l7ChjP7:%Do&2ITm1!c@M4lje"chX)6# + *\GK*[!YU&ILI=(Q\,Z@=do^VqOe%"14Gst\3&KAt\=M6(`+M%2bP;=clb$+t+FGN:R-3,,A`?k"kZ[LSKAi- + "a5#ZGG.=06\td7+&el*P*ck?*o>V^W`3X,Sj3=uDo?A/Kikla%e"ec,ogg1q8n!;?=fPr& + TDir@GS]Zrgbmbm^0T."`90.4VGn)r-a+4"#bSLSk5##NVi!F\Y-MPY%H^lO*[3eo&L`ug+ + 5cW]@=don7)_:Gr8r7o-JEV&ic9\IQE/';IJOeD8@=f-,q(=+$pL\#T!*NQN-H$'C2-k;]? + 0,*Fu?k;pg?T'2!B,YH8L*0loF'^:H&3c + /UR-/O_)^-7jlcN#J1Dk+#HC4JPLU`7gJBM!UD4TTUoIj)j/.aRdU10]ASerdq7-eS?Y:uH + *brS4>_F%.bmd;j6"ktC^hH\h+3c&\=n,0rdDlgZU\^GathRKf&WbBN)JjgDe(6kG]=*A]R + q#drqpWb==BDT0p;u_%#?cZ"O!:frfMKtBL&[AG^bo:[^RsYos6['+anrQbI41JNJiEWimQ + Y2f%DQDhG#(ZJs:;o=;rpV'#+F9_lcO>F4GtI`DTCWWACh(#oebhig?F1C^#aN`:R-0T%#) + LmX$Wlm7)GJ5YHA)'lLPShkfO\C8kKIu@U8>(ZW-hI]4<$3N'87H6TXLDsIOY;0bl['-I#=;iU5319Yu,9t4?6de/N + a_?-G%'c$Fo)J!RpX]Vl'kc[>Mb;.mII/oe"(WJst1K`sg),o-$qTANR0Kc[3U + ]@^Lp(?eWT@it]+/sdF;19[%)5@j!n94@L+=N_'FbZVpN/+9(c`2;QMg?LtX#CoCWLn)[/MJP^Vu`B + 4Yqgdf#0hRRlUNksd=Y6Tp1Is@tgGK=j=>b\*CL"2M?K`sg)9.K(,[k9\UXjc95a\mcMRA\ + *p,eG>QXOGuYQ`.u2@/K%X@=_7#7`@L)pNt#mEM>n^c*_0To;:q(*WgTI]H6,hYGM,.7O#9 + )I\j863;o8&Gs:kojP63VlKfHg$qBFqh+Kj5n:L7Men&.qG"ueP?g#7@Q,3Trnt_.g*d]FE + 2uhe"dd`)$VJ(f-.c(5/kC0s0mcO$tQ:k%V+T.g-1!ca^SV0eB]@2atb=r2?*ZNMki^JAdN + B4D]ol>nhU*ZDE<[fR2.\BTncVmUci=jl4eC:]Td:ZmsE3=E'UB"LB3iuKpFd^jeUN=+rX\ + r7D<'I!bShGJ"P^!>E.AXZ2p3Kq/.d8/P]8KZY + DPo2Jfgp6T1(cL7B:0P3!'!TLdX6ED`)\9M\Imk7NF$\V+_eb87[0jQnh^8X6,fO3.ild=_ + 65XNV9?^H%$]6fFUb9\Pf&:PX!gLp$6NoK$h9geRlZ[#cQ4,?r?EOqjU\BJaFQ3m4C<_L(n + .4c#O?W/]*o]b2M_rmqLSWaIODPcbM*Y--+c>naL4tN%nYX$9X;8kZNftGA*kGPU1Et(TVW + ?*VI%`B'dZ!?;Zqt&oUT+i%HO2k8EPOsoC^WZ;uAL;nDW5h;JRWq-F[YoH^bIj.RkM("`$4 + DOB#?B">r:A%FpA[l^FmI7`DlrFQ?4aK;;YP-uf7NCRS + e>15RVcM'%ld'B&RD[Q99=uU]b@TMMAR<]s0u\E0LqbJi/.a.8="7L`I0L2J+?`V5'YcH[? + kesn^>_GpSXeS_>imn^bCmImX2JA,'G^tBc`3d-^C00: + 01tI[Tu3Ohq1Yo*H0'r6ZSqQ6%J1C+C>`)ot`76bFt[(#A7aUkcS+C\c)Fh<%49JWTY4(%Z + hGX(.qqMMt*tL>:D#Rp>OJY2XH;/K;kZMVjii]F;_cMSG.f![4g6`B=1m:Xc+0MO9R?1CV9 + DRI2^L(KBYe0(W8NI8]+M[GijVHVdfe,Y"i%kOmF1=0N%DR_QpO1)K5%cg'&/V<<]>o*8kFN9 + CXqTQk-J;haZ,19ZDD-3n.n`-kKb+:M;OR-3.8;sUDDpSp?bfO.>7ld=^o8O55FZcg#D]f+ + h]eOo@5?(35n&-apibk%SOc&eME_Wn1'S'C4Zh*J\5.A]mS3ks=W/F9d-V;a/===>^3"p7l + LF80_D#EIsS?_;-gX0:f[imC=it_-nV>%iCiQ,P5i@&rWHP_@#:P\5iD<81!aX=L#U)_5Eq + e1B/$oTohb/_-+EJlONY;.c^>N)K];%Do2'G^t_5>j5^Zt\?l-V8hF2m,BOa^g--lR_Q=,piF2_K`VC3u,?sei*CVi)Ec\CZ\$\#%fo!$e$G:pDDX- + ?:1l$b8VHLB%Wbt[/&V*\UA!;i"qafc]I(rE,`8%Ge]mVMFT//C3?FZQb32TO2"i.;Z+LD$&&P7WgATgrq + :9.8C*CUW[_1]0]DWrB^niO<0PTYd%aiP^fI%2/H3esLVa+DoQ_1taj9.>YRf5cC\T0e3o* + *77:C6W5M>O$ + O5Tl%d1!f#Is4FGXVpL/W^Abg\SbT+>dM"iaCSF/n$7:cgpiF0u5L0)AeUi"a?SJoGa\NNL + GBlcZp+#4XZ=V32:7LFb^Roa>]oo>/:bO`+bZd[_*Yk^L?`L:Hr&OdHg+0*#l]h%"`KMN29 + EZA`D6Qs`6/l&or@uJWM^1(K^Z;>.m'S7.:+%j@0 + 5@>ZW,/TG3rJti=V+Ar>FLB-1RD<94a%%.hmH05%sH+H`%rts00&==o)^hebT4G@3)gQ~> +Q +Q Q +showpage +%%Trailer +end +%%EOF diff --git a/testfiles/cli_tests/testcases/export-area-drawing_expected.pdf b/testfiles/cli_tests/testcases/export-area-drawing_expected.pdf new file mode 100644 index 0000000..17e19ec Binary files /dev/null and b/testfiles/cli_tests/testcases/export-area-drawing_expected.pdf differ diff --git a/testfiles/cli_tests/testcases/export-area-drawing_expected.png b/testfiles/cli_tests/testcases/export-area-drawing_expected.png new file mode 100644 index 0000000..8c8ef62 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-area-drawing_expected.png differ diff --git a/testfiles/cli_tests/testcases/export-area-drawing_expected.ps b/testfiles/cli_tests/testcases/export-area-drawing_expected.ps new file mode 100644 index 0000000..130815e --- /dev/null +++ b/testfiles/cli_tests/testcases/export-area-drawing_expected.ps @@ -0,0 +1,480 @@ +%!PS-Adobe-3.0 +%%Creator: cairo 1.16.0 (https://cairographics.org) +%%CreationDate: Thu Feb 27 23:52:48 2020 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 3 +%%DocumentMedia: 87x72mm 248 206 0 () () +%%BoundingBox: 0 0 248 206 +%%EndComments +%%BeginProlog +/languagelevel where +{ pop languagelevel } { 1 } ifelse +3 lt { /Helvetica findfont 12 scalefont setfont 50 500 moveto + (This print job requires a PostScript Language Level 3 printer.) show + showpage quit } if +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +/cairo_set_page_size { + % Change paper size, but only if different from previous paper size otherwise + % duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size + % so we use the same when checking if the size changes. + /setpagedevice where { + pop currentpagedevice + /PageSize known { + 2 copy + currentpagedevice /PageSize get aload pop + exch 4 1 roll + sub abs 5 gt + 3 1 roll + sub abs 5 gt + or + } { + true + } ifelse + { + 2 array astore + 2 dict begin + /PageSize exch def + /ImagingBBox null def + currentdict end + setpagedevice + } { + pop pop + } ifelse + } { + pop + } ifelse +} def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageMedia: 87x72mm +%%PageBoundingBox: 0 0 248 206 +248 206 cairo_set_page_size +%%EndPageSetup +q 0 0 248 206 rectclip +1 0 0 -1 0 206 cm q +0.254902 0.411765 0.882353 rg +14.172 0 m 127.559 0 l 135.41 0 141.73 6.32 141.73 14.172 c 141.73 70.867 + l 141.73 78.719 135.41 85.039 127.559 85.039 c 14.172 85.039 l 6.32 85.039 + 0 78.719 0 70.867 c 0 14.172 l 0 6.32 6.32 0 14.172 0 c h +14.172 0 m f +1 0 0 rg +243.668 152.23 m 184.707 148.898 l 148.539 195.559 l 133.488 138.473 l +77.918 118.504 l 127.578 86.559 l 129.402 27.555 l 175.141 64.895 l 231.84 + 48.395 l 210.449 103.418 l h +243.668 152.23 m f +0.501961 0 0.501961 rg +4.251969 w +0 J +0 j +[] 0.0 d +4 M q 1 0 0 1 0 0 cm +243.668 152.23 m 184.707 148.898 l 148.539 195.559 l 133.488 138.473 l +77.918 118.504 l 127.578 86.559 l 129.402 27.555 l 175.141 64.895 l 231.84 + 48.395 l 210.449 103.418 l h +243.668 152.23 m S Q +0 0.501961 0 rg +116.219 153.07 m 116.219 181.25 93.375 204.094 65.195 204.094 c 37.016 +204.094 14.172 181.25 14.172 153.07 c 14.172 124.891 37.016 102.047 65.195 + 102.047 c 93.375 102.047 116.219 124.891 116.219 153.07 c h +116.219 153.07 m f +0 g +2.834646 w +q 1 0 0 1 0 0 cm +116.219 153.07 m 116.219 181.25 93.375 204.094 65.195 204.094 c 37.016 +204.094 14.172 181.25 14.172 153.07 c 14.172 124.891 37.016 102.047 65.195 + 102.047 c 93.375 102.047 116.219 124.891 116.219 153.07 c h +116.219 153.07 m S Q +Q q +39 17 151 151 re W n +q +39 17 151 151 re W n +% Fallback Image: x=39 y=17 w=151 h=151 res=300ppi size=1190700 +[ 151.2 0 0 -151.2 39 168.2 ] concat +/cairo_ascii85_file currentfile /ASCII85Decode filter def +/DeviceRGB setcolorspace +<< + /ImageType 1 + /Width 630 + /Height 630 + /Interpolate false + /BitsPerComponent 8 + /Decode [ 0 1 0 1 0 1 ] + /DataSource cairo_ascii85_file /FlateDecode filter + /ImageMatrix [ 630 0 0 -630 0 630 ] +>> +cairo_image + Gb"0WGFVoNIH^]DMs@>:hKuG,0.X(r,f,]L#qUH]rg8Q\:I]joW(", + UT5GOfW;#Y*)i!>1tE8h,\O.4@G&jg$c)%$beRNlaq3MXV8gH@>\(UFekStW@gX? + $.>Co=LtPM<#'4hP%tkD9FtKkDeb!,IV_T%-_@a8<&7bW2mqr'uX$_SS)C(4\9_G0atVltr + 63+5F1fr249KKnX.f/-'IGmJ?4e7NHbiNqa.9"ptaa:fm_K*.NCV]nLU!fK!g;d=C*N6t,$ + l$4FY%u=\1nfi>Dt1^ + m\2+-c]BBi2i`IYj`'T"^oUP>:.?sfTqd:L\ArYrL#)c4@H":H/kFSG,bE^)@6Kk + Deb!,G>JSmYCI?`g+`OtH5\;cn:!_?-t(bbjoBKtY0_@`tR;K*!PP-nIJJ@0'J?SC\OS2iU + &+DkYKA1PLNq-nG?[p)IAsZEPp.?:dFki>Dt1^lH)uZE4i7N5$kl-nIJJ@0,#cp[E3-Klrp + @k0lp0$4G0V=9*O241*4@@F%>C;XE;Uf]sWK_SS)C(4[Dc#;$bc[S>cm^$"cT$470e:fm^@ + j;_9:^T^SUG0f_s,mB!VB.,;248/,Mi_a,)-['aBP(LQf1DCUT*W'mnHWSiU^KO46eG>5#@ + Gr(2$c%c)q\FH\fJC!<;G9'SfCDN@$4G0V=9*M(mIRon/WRJ$a5W.S@Gr(2$c%dTHPZBT=N + Bs`Xf\ClYj`'T"^oBEpZD?R=N@CXPP6*'n>$$k$dVXO!-T?.GFkF\Fe%<)@3l)2KkDeb!': + \(qiK-X]ku5CVsJ;MC^LQr:fm^@*.DP'5[gGCXJDMnn-k!S@)nD=k"$6$/iB>:/JP=H_m/9juuu$4FY0UMnGD/aSnh8rA6-Y,%_pn-k!S@)oP.ZOE:I;%Knf + fctF]NW7%rYj`'T"^oBU2eEuXYYf%>f3S"?(qYnn^2nUj.k)kt4@G?o*NU5oS'DCV-=@keei>Dt1^lB'iV\ + qnleuoJIE,&U[qq6tqR8)]P^[k8@5;n4LB.,;24&5.0RV;\@p"7j.[]7d3Sc?CZc5t_'F3h + ,gIk46$i>Dt1^lFW5V^*1p4?_i`_E)FSVgLY]DIairP(LQf1DCUT*N-=l>Z`AKpFWJA;.g[ + /@Dt1^lJ.rS![mRcKX:B$gXbB=k6YPSCfi=Dfd9/K + nX.f/-%b,Dk<&(k)/f8GREce8rBa[=&MjsG_X':YiGAQnVK0,=:^CtYhlLX\6Q=EH4D0&;- + 3gA(^/,5B)I@"=\3U9pTY[jgeHh4jtDl=`CNT8(4ZR8Nik(7>H&[6:gq%A8TE_9m,fYo\eo + Tf=9'-'2(Iqr4ij;#/Lr7WCp0W_IV3t*h)aegN+>7`!EcT#8@Ch60'WILL%T+*7DY!U2_.s + %O(BRG$4FY$I>=+-'Wk('2_Jpia0O8LA]Mq6j@&PjFsgeA0,1E.c"/YTW,E&qc'AkWW#Sl + _rG&cfu*c,?qZnErD!JdSh(C7:?4!Y^c[^K7mOX]keG7dB(eiojGCoc.b"I\h%'3:a;ba/- + $(Ok:ACPf2^YU@UZ$68V,fmYQ-46Y]$4$]IJ%7gP*?.]'a<_-,i/N;BSgN(4ZPnpF\/@A/3bSVFRRH9#4?n+)^m6;$]_k_f-!u!\s#@4FYr<*p/b/)5.UYR. + k0s1'Gc3&8)B\E:oKkpUMkk"'jXCN?4X=\ft8mMDHpF!4in:0'Gc3&8)KhROJb.q_9^!,W6 + i6F7b1FcC7b&O7fZCg!#?R14j+sA%D'7QKtS4,8Wh%c$@%TJ!jLV8)`%r'e>P/l$@=;7P+k + d6Zl@\>Dbb/J@3l(\;-3f6*8. + jSM"$s>LA]MA_'p\C`6Z+G-U[U7hAL!!#?R33R&[9:>fXjgEO4l_XbaQXJ>iM"^o<+*@?IT + DelCWh9#LcMW/iX2qG*r*qBk1@0'JLEr4?S"-f_(LkkXu%D(@UlROE-:;0l=;L6VU$AAXk_ + 9$Om'!749bpZoK]3RRI5kY^>2]rrrtQef`SM6o')CLSH;FHtapF3XM=9*7+3OZdI0r93#GWOmJd7T:n\olF9J9NCm + !4VYA2(@m;4\35T$#.nW7=(?Wa;4-0<*-J"3X"pXKa$DchBU\XEOi"QMFTI"&J%o?jDH#g] + l]Za$fD0q$c&mXLR/sEV+_C20;]Uf;BS.;(4]Wd%P9JMasGk>]TpL:FdD]c'W(\=ZB?EB:f + m^8jW%N?En.&aMMKUh031sbm[@C1A$pCp?jhYli)\-S,mH6;XS[fTdNpPI;WM\c^e0Jp6"' + f([=A3cK9T@!hVsB60;?pc?PVosQB`0+K#.S.COaVRK8s3a]u9e]M\dlm/f^>9gr.7=TgiG + k\922O8RX,l:[p2DjN1#OG?@EKC(qG6>6jmT1rtGnh'\d_]0'7gEO0\^F=CUP_?+]Bi1:)V + mC9%*M)WhnLebR\#M>#PmcO&k8sW)i!X:&(!4NHO2r>_tf6(0b+F:tiPnYV.h27"9!st[*! + 8KX-mQ?,4gsB?F;s55T<`51KS^Gs&a0HjT@0*l\pmm0KH;UHD`&m1^HJSS%*aq"5XJ?+:$4 + FYO,B(mcLdeH6/07*\21)Tk@`sGiZ>AcK+A>!I_dVMNgPbo]l#JE5!M'Zlih^"h8gAF26EQqNH@\[+;>T + A>K:p51oNcoDC8aRT_W=\02j'Gc4'a'YHId"Q)&0&AZ= + d3f+MN3&f@kZh.d":Ttfi7@GMV]->d\XF0bSY0_gQ#D!q7HrZsbmiJ*'H@f&J6OCE:,BtBG + iQ_"k]YNY;OlQ#V>`M+.0>+Jc(Gd%eRl(+@dWf0!)3,T@'u^o^O>-RU + tQ$h['\W!,ndmSISD-,mDRV`*,/Pn$O2U9FXQH@O,V6mr";4[Qep!S!o$MY0fXOj2cAZ*rY + #l.2XW$4FYOe#t'&fQ2AKGg:miU=77I=@l@iZpZr6.>YhBE9m/Y$&Qt*,!AW4AO.);8R_F) + =]SEs=9,%9B%-NCVgYfk.UKB1X3mL0ok:t"/_MqTWICC[G>\[""lP]_W%Z\R`=j#7?EBqNg + c>1B'i,10YQ.!tcdFCkEm\5n=Zj=,:,/^$J5>;1 + <7d,dK_I6-5,'!cV`>iT1:(rLt>USulsgY,&<"Uk&%sZ^Mh98O6bc"Ot.Qb7&!0RI2`bXO) + g,U_mf)ZKL)7g/,+F\jXYt_?+[&kFbCTbB71^@"hIAc7%(-lp`m_gVfQ(/#6[>PUJ'_=)nd + %243ZqFW^-WKiCIpoF4KUjJ>R-nG@"p?u?t0[_InDt-e\RR\kW,0ep?6t$*! + `VuCH#/g@rmNipRYcHZIPE^I-=@mN+N@FNZ<_K + >?UaRboK$4FWs + 8)KhR0W=;[Hgq*4!fpA[goOa0*Nr;4YQ.og[X4=VP]"k8`9WU2,!,+c*agq4\XUWH.p4@XY + Mc1[!*hPX`_;!sm9EkPV^Jr)nDF-Fl\O%u_F@O4IbhAK#sHn + 7p!!I3W!ldH:iF$up_(FKC:lh4ua#eQ3NgbkLb'GM=5(Ja=eAh]Nh;-5!5XJFK(Tt2dZ3bJ + I]@0*lY@`Qtd#BHsg.it//`_$RF$QoRIDP**]-'')lSJ\:(XWJ^u%9) + \G4>Y.4i!OuSmE_H$9nW_rtRWWqU*c*d@[oZI48thLa9b$EkT3nH[ql^\LJ?_7-=.&p1TcP + cA&[][Z8d\)-R0DU;o2J*%R``Ld-$s7bDSH&c4+Gr^b7k_BZ+WV\]q>U%(Bh!Kf3RuaYGA[ + IE-@DHUi?cFl6GZ$*6o)-q88 + =0%4ES3J\XDp&7OoV8Vjgt6ga\\X4Ve,@8?JN,OON++s"DIi@J9rtH&iC]!K`nDpRQ_0^jm + ]F!BEDos>DX9Hlb[A4mdf%,%JbVZU>+#UG.@b0=q.RdAZ#8s:P,3#&Y#8@%f&[lVC+rr:F^ + OZR+Al.M]CdNT"El([,dO,HC=Ul47E+_a4T:_b!cTgU^_2:A;ka)EpAtP6Ff0UMg2:GlKK0 + j*i^n"7gp$dtL#l)($aqFq$^AL0Y\X:<`h)!V9*&PI3rQ]4A2gf>>:/.8\D5]ZeR*gLVJp1b%u6m`!.8`nha2t; + ;lP,"B*'\kdClV/WVk8Y87[@N@K"`cCiX-q/;R"1'4HQ + 6R>_hJi@4p!gWq]AraR%>=rs":+\F2jc_ZF>r_N^l%>JMNN)"FRIDQcBY:(YVt%61]*IIK` + r"jLYeM50R91(+7V2'Q6r/"ik\4okGluVXHAKb"R+@\*c2a/N%CD0$CuB'OliOV%==V5[YUXCQ?6l + \Z%ZJ[f@I`d-]-CiHA."Y]E_8Vi?LMciI`)\Mc + 1VclM9A>*a,ZCf'(BkSbAO;!=B4MKk%`,(lY4:SKU2pi=@SB;?TCj*`oFu?I?IO*ct`iKa$ + DOn^gL,T%U3o`@!lt'4"0a6p-:6jQk20omlA>C[@BBKa$DOnX\,;B%1bt9c?o[o5FQ4mNWc + C2i@73$JkT1Q`KjL@0*lW@Y[N``JAMA,iT.Er#1j%eMct$c%Hp> + :1dZapRbhnKAEGcF)U74]&ek/XNO4q3I!l'Gc3,Vm[k`)n&S('VFk#Jh+F!86'a(fFg?cq/ + $j`C(R$)#`*5djkEB7k?#8Nh*8&\g"8W"!P!)pF2EV_d_j$/4+bA\OE> + s9nh.!jhm0M@_?+]8oVR9`G[7D+M,/6sMY35K/`3kEVRm0'2PI>T>W@rBUVr[*.R)8m4;Ls + q[!^%D5YiK!YQ.Aa3OZe4%$p1pntQ_HFBs)`Fa\pJ"'R?ZrVi+B/JJtNWI9Pf)&2e-mO(6q&-b(8(XJF7G`'?>0U6]uWB + jt9F^Osl#P`hd!\fm\@#70X0C(gmUCZhpK`tk!s8KtAKrr_8j[UEN!t#FOeG^GO_HtX'j@: + n0J(E+!r8p?UDI=MhV/-&TRpFJ!Hlo!TnIe3Y)TWeD + i*I;Y<6"-t_Y4A=un,\4H@-Z+No*btQ6[+Y7I=MhV/-&V(hg*;9YVeI7Ie3Y)TWeDi*I3"7 + 6"+POBDKp[pjg3_0QlHY3.3Kl%+=j0JcQ-nG?sa**jb[S^\[O$:>a'Gc4uj0-c_-u?4bQQLX+%k&t/;o + `7;DG\uA:6QcJJ.F%Z!)k7g:,=<"r7hDP6"'e]3k8a8U3Y#6A*)kpi"$/&^spKn;\CZu^)e + L`pAf\>=9,3nD+hCC"a[QRg]RI?@0)`V[<6MaKe:e!jl[]C%gi][eGEGs-/GtVVh`IA4hr-R(4`C5RjTIbfHNIPIdR79TWeDiS_5h`dD?[bU%378n-=XN?n0R: + 3_K'Qq7-b*K>I[DZKLEYK>O53KB22:i"$/&^rY@Wf`m/DTEiWc4hr-R(4_O\LR/qop7`TN5 + PG/-:fm]ep)R=K:tRPZ?[hDDrh-IqN8ts8.;ZQ_.mNb.Io'5I7WJti2(7g&h+)q^!WJar!0 + $:%^VleL'tUmPg]RI?@0%4uTI[DEa[cKK>LsF4ltiJi"$/&^rV)?$Om + GIWI)_44hr-R(4_O"HPRG4[Rtb1T6l)Q-nG?CLYeM=:tY*FX8L`Tr'=`l(ch6V4G1N-cs>_ + q*oIP9$c'iWG98RgCphQOT6l)Q-nG?CLYnM8:tQ]B=aoc>rh-IqN#[+YN^7)hFXR!q#P`qg + !\eZEW3CEUKDaL,r'=`l(chHTCkKUUlh9mj&+BgX"^pEDmTBaJD:%\2k8rOl$4FYjYr?85' + XgkOb0N5d*u#K@OlO&c(_jDMB?8C^^`O7+JnBbYDs5Y7"4>#^7hb2'"@&f$A63]lG<$Y + &+C$^"d$7-7YSOK#K[K>^c)rC5iVJYaYkR676NnbkQ/DkYQ,=tI\ + :'-u)&2e-mO(6k!8gUT!/\lZ\C@1UjgqYnci\Y_@0%W+kJ.^__H,kanj=tI\Gc:@)\ + i"'mNk*i!8gUT!/]I&ZcJfD=*7=7Ta1@I_?*9`JUIn8$@m$4@trLYag9luh,aIcpgHl)t$^hl\sUrsG^MT]b?6+1"KDa'uHp_@m$4Ge$eR!Orll/mi[^AOlVo]H4^c)rC^ooUSXtN!o06`WA9@m + 58'+aRGK`r$1j5TY-mB40^e$_A%7Xj=tI\O&3b%n"]GOb/Bn(R%dVh`m/D??el_?-^]r>pg + (jm'*PFK\nr!fIa\@=_BhCQCk&MY5>B[S.fT;-4qE75e0^0$\miX#laiq0G`"mOg``!'q;7 + 828]i8DL7F%q^5'5[f5n&S^+[`?'jt($JLRFBA:["p=s\"]BtsHR)d#e-"?]#lFm\"d5G.H + R'SbH=W$'/3hL35bYDI;S,f7]Q!%En(O52a-Gt>X@!-YK>I[]MEMT&0FA15UDk#8ZUY'.7U + ^l/dSR9IFRMn!04!6U!Q9]/!_FWJ7j;lp"Rk'e*XNqi'Ga4Hm#_8LLVa*9kK=p'dF[%r!@3 + Q>!b'(i#p?`+/.bSCOT9un_?(l[I4\_X8.\+,6jGZD"LQ2"jH?abdk9EnDT;,Voc88 + FU8Q7&B<#5@f;/7:?5SYt"jd#o"0EP:q8.Y3BipZNU>_"oNE4lH41[Vg<[RarUj35U\2A^# + l3bjep`U!MgP>$[@Un&jV>KY^gAT)qL>rSa5NI$aW>)#VHGlG_8-l5cUT`J8G`QCJ+O`6QLrlsel!5aZ2!XW=s"G"!Y=+:db#!0AlA$abaR&c`>;=PuKt,6>Rh$4GW"HR&U$7:Cc)`Z>EdG#6gn)B(M=] + XXu7@)[s9b75Loin1YS_2]Q-[r.c2lDq635PicdJ,"e5NdpuIBeB@S?i4Ano,hr9Z`;)V^GOFE/2qEldj.L6HjXU[]:@e&*Ps#aFW^uNo2E,P&.9ZK?gC&N-e(NXUhUFdioC: + -h1\.fkL9+b>Dr/,JUITOreh'g'4WQSZ%iWr;lI;u[QhULXh]MXHcOPHp`NfI?Io$'1J!mQ + eo0Z9kCMMFWs08@Hp.,1f/7f08p$*IYEmhM6r78>(54QRqN>qc@T%uO[/&Q6iXdktGgGqLQ + UQ5n4qWX-f:[@r&c;2aYYGZd#,B6l\@?1PQ>*?7sV55]OCmk%bDbp;p7gDhZeu`H&FQmVQ4 + _\VFaH2Etf(=CY\^CHGs33ZHA8-JjHLX5_,etH-!+&6S_E+eZEFRU^IPg)=(IgJ&Vu?L:OR + Aea-SmPqe>U[<5OZcKO)qIFSNhZr7/l(4\?JI)J;6Hqf^n9-3a$b)l&gl>okN)29fdKL38M + /WY$BZ#$1hgZL$r7_7V9I/8)hQ/rr4\oZ'1f + =](=0[B7.=Yh+4I0'WK(OT9?)+:DC[^A?A]B0Yq)YQ4i(iY,5W==:17s7r/qIs=dL_O@/t=/Ga + B2+":!8ki?$G18lO/WO#.eB4^#7'6F\s0s?Ns1@dPs7tp,s4aeN%9BL,;BbjPH.K(bhu<>f + _SR&ITet+m];B]-9M[QJ.=6F0KLj?>J-lK-*m&5mR",O86jbPjkmBE_SUJ) + :S(hbbVLJjpMTHoh!Z2qelrshHe0(0;s9:G8$Bl2+SqMp.&Hk?,BnT\>-XgJ:OY(Bq/&_dY + ;`L_(;2TMdnA:#H6@P[!$oQ^@;k"-s+;Ce?b!>fC*;V + _u]PB;#=hkn-WKi(bB*rE?8,"qp;$6XFq(\[F]e_g/A\:4C(q4ol@W^mVC3'#Wo$Yk+TESgS[:+`3A7\LL'&MWb@70HR- + U;dCjF;kmG4l=G:U0^4"fuIXDr-L2UFqQ7MW@dYU8JQ_HEN:\SD'@nUIZY%R^e,LK9crD5:mli4NNH.?&hkM0=C#n.H?=n*-LMoWd++Rfm#B(Ok4q* + WcWI\G9VRuo74^GfJ.mLm-^1[CWGZ+XIkX##NGKCkg&6tb3mBob'Gfpn`f(TgJ*f)`=m'`DA*-tVr@lIG;ahjJlTpLe9FYX\a[;MZ*sU8$ + XCs7B6XRJ+AP3[4DpOQ>''O*a$de9m`E6qs6+Fm%'G54"CkX%1\)cLUEc!3)X'qX/RBgT+C + /4+f:OH@*jLddnR[pi:J/V7hrU="ijIiu^d;e5l96!:ZJ[n^p&%W46K9B0Q7fEqDFW6^3k" + R@3d=&+hjNmpk_H27c;PKDaI55Y?$'fIkS?['JEtO"3(_=ZJXVB"[mdI8Bgi6rd797H"E$>J3\q*Yha"DN;RC3f''>Sr,`$fA5]?,NINc)"=32Z:=s]Yd@]g:=4j>d8no + jKMo^QbHl54eQpGC(,Jaj-p].=3G/=ETbrNl..#l + sDS'_\kF@Ge'gHQrV/D;2\Z8!l7P5!J\*nVo(>i!kHSi'qd7ci.fdp1(4l+P;N& + fO5g:4LcLNp6!5q`:"P.4M)Q:kTT5JE@T1cr0!2MR]M]hnc;V?fN_TU/"s5T:mo&%l60`WY + \_BScHE%XbJg%h1aWp8K@6\4KHt6AT0EQeT[XII_,VO.23Pko5aK&!L_*g4T,c-+q)15$B&Hf-'N\JSm8IfH/K\ + C.jus3fN4=VV\2-7b>sV;OFC[@8-0J@amJCdbu'=hO"=jj0`]fR/5j;fV:&=0kHSk&\eorZ + 'G`.WObCKgo@/($+gM*,;-9WSO!OYI]4ZRoqAfnc]I5=/asWEgY7`nRpo*hS'L52d=JVB]. + b-g2FM@^oddL*7@74OdJt>f=Z\ndYV5`oH.K*9N&3:L@WTbk.\uPOd'ArJYV:PX+@Ig9ggF.$6:Cl+-nGqY,: + `]aa?gX;Ds@!RJ0:Mh5Wf\Trm(Ok>l&jX+:'AH&T.AFl>j + =mr8>*-nLIYQ]rDii]O8So&Ul7sTs0&oT3&%FMW]*-;Wl_!!Mfu.$Wupb($ + BjaZ1PXLJ3PG5@*JE;U6h,`Wo?&+1Y%o^9>CE+G`'?>G!-`3%Z9/3-iN`Di$f!@=LV8KH+&/hH3+*9%YY( + fHV(3N.\8G1W+u6aoFM#\&IJnjiKGb:o63u&%flYOTs0&;VQ4r:YHFnba3o#/J/9Ubi/q[4 + ?)@>g#GgM_!&%/N^cN(-GYYkEFm=>%UB#ge'Gg2`91nkTnXL+@aGf=>q1/O]+pn^&l9d%mP + Er(pp:l?Hl2J*%PPeM_?j25f]J`*qF-b.s3hs_U$MDK`\kdrr00^NY\3XO(YC0HNF;<-tO1 + uUa'%n$hH<@$3fq^Fb#6b)0'Gg2SC_b9!1o'Z5Kl'haLKreJtLq:E + A$HJce)o(8<-H9en+_ECa]3)E_m+3RVGB@b!r%k;Cr!^3PgpZ%hn+Qt4)i/]NAeOd`$IdlM + f@RdfF=ZrKGpMS@FjN6"'edX]=n]%kR23^9Dh<'6Qb*0i(8.g) + L'9Pf:_Ph@inc]/R(/!q65E"oPi9`H*+#?r-*BD7q=^Jf`#($G]j[(@^4>ESXTaeL>qH^Q7 + n5Z7YY?BHH@>G_O!9,2uhso\:hkHrioahMM/3P@kQ=p + S@0%hI4`KS6IW9n1Kl,5C]H[2;+$QV,9MnnWb3,!];_cV*E?5!2Co`j,qQRU60L9+J6/ts- + k83$`Y^OBbQ3[99kmm8_$O@+I$OHT=(!l^.`ZF,nD8eY85eJKr+DZdjH+IUa+8J]>mrsWob + :A)[-nHM,%ob[Sn,I.hff,da=7ol^cj+e<$4FrORSm9McmQ@8;mS@mI*GY]$s/ + !/g?/.`@gWd)f"IP5aPQBeK_6 + \?B%!EIV&ZOUTd"VHpGY5)&[.T0!2E#fbR&R + 8X>Xg@)D-P-rF"TiA-MKqDbnKB*1D:Pr)6KE\]jgqYnn-!3pYWr3eSr.gip[?Ja]KnH]/aB + LBnp,N"3B^VrGQKTs=ENrC[C=cE#/jB$l0U1i]>m]IYn1;6=G + Ef>HD;*c[9?7GTts">7#FPpQV;E^k5Hc+NQaH@\,#I\r-70]s=DS%16=>#Kon?i'AbB&7f. + .]fn/&9nhS2Z#.+%I[NK1oCU[k:6Qcq^jT'^+Q3*qQtQ$QLVskj;H*NTZcD%Tl;$o&'Y#e' + $O#6)(%ZhGX(0']l(6SVV[D6aIZOX*7:Catqk/glQ7_DShFI=_:X1aECp>$.5=gA&U9HnuI + 9slTSN:?3f<5udUeB7)Z4./1X2d*`FST&Hgl'#N_:M]+4T"h((/L`!OerS"&%nso=n8m5\/ + 0i&Jo?g0H2crEVMHVJ4gRZ7Q0Z0TZP9ncKa!'HNJ`"QG[d%b+5NtWZbW6HdA:fnk8?AuRA+tG1=\TrgP,2J,m1jc%SFkp&HS-!o + .YWsW/<\[C!IcqlZj!`_Be"EpVH@YoQ"n"]cJP_0_AuD:"ClW`^h7HjpaIobkHBe=e"n"]c + JP]IO@\.LcA"7cch5e/rb3YnU#2N%'U&oP5YWsW7To+-pi"$$LpX+kMm?7qc>G\sT:K67I' + G^t.VcCqsDHrKP*3H_qBT>Ae0Bb^m',A"jK`sg%AYn+][SM3q>'9L!nNONcDXu6))[SD%.+0%RV$4DO+C_cR>gV:uIn"UVOY>7&)lkS>LI]* + =M-nGoFlVu+"c#8UYO/!GVN]goLfMXk#I]*=M-nGoFJ#$PF:ObDC[M(`?>Zbl?fK;;TU=82 + d.aWda!b(Vs.csmEZB'5MX_=_AQb1B#8Tlu&Zi"&3>#8P3-ltl/R[_mkDDq;=p4huZ5,]&_=* + ;gor7X';5`[t,7hk9_-$i+]*dgfq.+eDIFRMiXo:#)j?$EF%?#"M[HJP:bc7W$t+@I!GR-0 + 5hS08qD>>!bA@8PTL\tAc!7d)g[F@p<;HFpAngJK$jrU-;?*uY.=Zr+e)YIp#,:Y9B$-$Q' + !E-&0>`5aaPV+G;\=/iY!g?L?'Ip%56!/T*6.d$QF-\dGDd?^8'=.2jl7\(tlZk4&cSQ;hI + >0e^Q"[_@t<6V[\9jdEG=qfSV[q10-A@n_NM9g$l7ChjP7:%Do&2ITm1!c@M4lje"chX)6# + *\GK*[!YU&ILI=(Q\,Z@=do^VqOe%"14Gst\3&KAt\=M6(`+M%2bP;=clb$+t+FGN:R-3,,A`?k"kZ[LSKAi- + "a5#ZGG.=06\td7+&el*P*ck?*o>V^W`3X,Sj3=uDo?A/Kikla%e"ec,ogg1q8n!;?=fPr& + TDir@GS]Zrgbmbm^0T."`90.4VGn)r-a+4"#bSLSk5##NVi!F\Y-MPY%H^lO*[3eo&L`ug+ + 5cW]@=don7)_:Gr8r7o-JEV&ic9\IQE/';IJOeD8@=f-,q(=+$pL\#T!*NQN-H$'C2-k;]? + 0,*Fu?k;pg?T'2!B,YH8L*0loF'^:H&3c + /UR-/O_)^-7jlcN#J1Dk+#HC4JPLU`7gJBM!UD4TTUoIj)j/.aRdU10]ASerdq7-eS?Y:uH + *brS4>_F%.bmd;j6"ktC^hH\h+3c&\=n,0rdDlgZU\^GathRKf&WbBN)JjgDe(6kG]=*A]R + q#drqpWb==BDT0p;u_%#?cZ"O!:frfMKtBL&[AG^bo:[^RsYos6['+anrQbI41JNJiEWimQ + Y2f%DQDhG#(ZJs:;o=;rpV'#+F9_lcO>F4GtI`DTCWWACh(#oebhig?F1C^#aN`:R-0T%#) + LmX$Wlm7)GJ5YHA)'lLPShkfO\C8kKIu@U8>(ZW-hI]4<$3N'87H6TXLDsIOY;0bl['-I#=;iU5319Yu,9t4?6de/N + a_?-G%'c$Fo)J!RpX]Vl'kc[>Mb;.mII/oe"(WJst1K`sg),o-$qTANR0Kc[3U + ]@^Lp(?eWT@it]+/sdF;19[%)5@j!n94@L+=N_'FbZVpN/+9(c`2;QMg?LtX#CoCWLn)[/MJP^Vu`B + 4Yqgdf#0hRRlUNksd=Y6Tp1Is@tgGK=j=>b\*CL"2M?K`sg)9.K(,[k9\UXjc95a\mcMRA\ + *p,eG>QXOGuYQ`.u2@/K%X@=_7#7`@L)pNt#mEM>n^c*_0To;:q(*WgTI]H6,hYGM,.7O#9 + )I\j863;o8&Gs:kojP63VlKfHg$qBFqh+Kj5n:L7Men&.qG"ueP?g#7@Q,3Trnt_.g*d]FE + 2uhe"dd`)$VJ(f-.c(5/kC0s0mcO$tQ:k%V+T.g-1!ca^SV0eB]@2atb=r2?*ZNMki^JAdN + B4D]ol>nhU*ZDE<[fR2.\BTncVmUci=jl4eC:]Td:ZmsE3=E'UB"LB3iuKpFd^jeUN=+rX\ + r7D<'I!bShGJ"P^!>E.AXZ2p3Kq/.d8/P]8KZY + DPo2Jfgp6T1(cL7B:0P3!'!TLdX6ED`)\9M\Imk7NF$\V+_eb87[0jQnh^8X6,fO3.ild=_ + 65XNV9?^H%$]6fFUb9\Pf&:PX!gLp$6NoK$h9geRlZ[#cQ4,?r?EOqjU\BJaFQ3m4C<_L(n + .4c#O?W/]*o]b2M_rmqLSWaIODPcbM*Y--+c>naL4tN%nYX$9X;8kZNftGA*kGPU1Et(TVW + ?*VI%`B'dZ!?;Zqt&oUT+i%HO2k8EPOsoC^WZ;uAL;nDW5h;JRWq-F[YoH^bIj.RkM("`$4 + DOB#?B">r:A%FpA[l^FmI7`DlrFQ?4aK;;YP-uf7NCRS + e>15RVcM'%ld'B&RD[Q99=uU]b@TMMAR<]s0u\E0LqbJi/.a.8="7L`I0L2J+?`V5'YcH[? + kesn^>_GpSXeS_>imn^bCmImX2JA,'G^tBc`3d-^C00: + 01tI[Tu3Ohq1Yo*H0'r6ZSqQ6%J1C+C>`)ot`76bFt[(#A7aUkcS+C\c)Fh<%49JWTY4(%Z + hGX(.qqMMt*tL>:D#Rp>OJY2XH;/K;kZMVjii]F;_cMSG.f![4g6`B=1m:Xc+0MO9R?1CV9 + DRI2^L(KBYe0(W8NI8]+M[GijVHVdfe,Y"i%kOmF1=0N%DR_QpO1)K5%cg'&/V<<]>o*8kFN9 + CXqTQk-J;haZ,19ZDD-3n.n`-kKb+:M;OR-3.8;sUDDpSp?bfO.>7ld=^o8O55FZcg#D]f+ + h]eOo@5?(35n&-apibk%SOc&eME_Wn1'S'C4Zh*J\5.A]mS3ks=W/F9d-V;a/===>^3"p7l + LF80_D#EIsS?_;-gX0:f[imC=it_-nV>%iCiQ,P5i@&rWHP_@#:P\5iD<81!aX=L#U)_5Eq + e1B/$oTohb/_-+EJlONY;.c^>N)K];%Do2'G^t_5>j5^Zt\?l-V8hF2m,BOa^g--lR_Q=,piF2_K`VC3u,?sei*CVi)Ec\CZ\$\#%fo!$e$G:pDDX- + ?:1l$b8VHLB%Wbt[/&V*\UA!;i"qafc]I(rE,`8%Ge]mVMFT//C3?FZQb32TO2"i.;Z+LD$&&P7WgATgrq + :9.8C*CUW[_1]0]DWrB^niO<0PTYd%aiP^fI%2/H3esLVa+DoQ_1taj9.>YRf5cC\T0e3o* + *77:C6W5M>O$ + O5Tl%d1!f#Is4FGXVpL/W^Abg\SbT+>dM"iaCSF/n$7:cgpiF0u5L0)AeUi"a?SJoGa\NNL + GBlcZp+#4XZ=V32:7LFb^Roa>]oo>/:bO`+bZd[_*Yk^L?`L:Hr&OdHg+0*#l]h%"`KMN29 + EZA`D6Qs`6/l&or@uJWM^1(K^Z;>.m'S7.:+%j@0 + 5@>ZW,/TG3rJti=V+Ar>FLB-1RD<94a%%.hmH05%sH+H`%rts00&==o)^hebT4G@3)gQ~> +Q +Q Q +showpage +%%Trailer +%%EOF diff --git a/testfiles/cli_tests/testcases/export-area-drawing_expected.svg b/testfiles/cli_tests/testcases/export-area-drawing_expected.svg new file mode 100644 index 0000000..4c5abe8 --- /dev/null +++ b/testfiles/cli_tests/testcases/export-area-drawing_expected.svg @@ -0,0 +1,65 @@ + + + + + + + + + + diff --git a/testfiles/cli_tests/testcases/export-area-drawing_expected.wmf b/testfiles/cli_tests/testcases/export-area-drawing_expected.wmf new file mode 100644 index 0000000..07f7e13 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-area-drawing_expected.wmf differ diff --git a/testfiles/cli_tests/testcases/export-area-page_expected.emf b/testfiles/cli_tests/testcases/export-area-page_expected.emf new file mode 100644 index 0000000..1a8b98c Binary files /dev/null and b/testfiles/cli_tests/testcases/export-area-page_expected.emf differ diff --git a/testfiles/cli_tests/testcases/export-area-page_expected.eps b/testfiles/cli_tests/testcases/export-area-page_expected.eps new file mode 100644 index 0000000..acb7604 --- /dev/null +++ b/testfiles/cli_tests/testcases/export-area-page_expected.eps @@ -0,0 +1,443 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: cairo 1.15.10 (http://cairographics.org) +%%CreationDate: Mon Mar 2 08:39:48 2020 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 3 +%%BoundingBox: 0 0 340 298 +%%EndComments +%%BeginProlog +50 dict begin +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageBoundingBox: 0 0 340 298 +%%EndPageSetup +q 42 49 249 207 rectclip +1 0 0 -1 0 298 cm q +0.254902 0.411765 0.882353 rg +56.691 42.52 m 170.078 42.52 l 177.93 42.52 184.254 48.84 184.254 56.691 + c 184.254 113.387 l 184.254 121.238 177.93 127.559 170.078 127.559 c 56.691 + 127.559 l 48.84 127.559 42.52 121.238 42.52 113.387 c 42.52 56.691 l 42.52 + 48.84 48.84 42.52 56.691 42.52 c h +56.691 42.52 m f +1 0 0 rg +286.188 194.75 m 227.227 191.418 l 191.059 238.078 l 176.008 180.992 l +120.438 161.023 l 170.098 129.078 l 171.922 70.074 l 217.66 107.414 l 274.359 + 90.914 l 252.969 145.938 l h +286.188 194.75 m f +0.501961 0 0.501961 rg +4.251969 w +0 J +0 j +[] 0.0 d +4 M q 1 0 0 1 0 0 cm +286.188 194.75 m 227.227 191.418 l 191.059 238.078 l 176.008 180.992 l +120.438 161.023 l 170.098 129.078 l 171.922 70.074 l 217.66 107.414 l 274.359 + 90.914 l 252.969 145.938 l h +286.188 194.75 m S Q +0 0.501961 0 rg +158.738 195.59 m 158.738 223.77 135.895 246.613 107.715 246.613 c 79.535 + 246.613 56.691 223.77 56.691 195.59 c 56.691 167.41 79.535 144.566 107.715 + 144.566 c 135.895 144.566 158.738 167.41 158.738 195.59 c f +0 g +2.834646 w +q 1 0 0 1 0 0 cm +158.738 195.59 m 158.738 223.77 135.895 246.613 107.715 246.613 c 79.535 + 246.613 56.691 223.77 56.691 195.59 c 56.691 167.41 79.535 144.566 107.715 + 144.566 c 135.895 144.566 158.738 167.41 158.738 195.59 c S Q +Q q +82 59 151 151 re W n +q +82 59 151 151 re W n +% Fallback Image: x=82 y=59 w=151 h=151 res=300ppi size=1190700 +[ 151.2 0 0 -151.2 82 210.2 ] concat +/cairo_ascii85_file currentfile /ASCII85Decode filter def +/DeviceRGB setcolorspace +<< + /ImageType 1 + /Width 630 + /Height 630 + /Interpolate false + /BitsPerComponent 8 + /Decode [ 0 1 0 1 0 1 ] + /DataSource cairo_ascii85_file /FlateDecode filter + /ImageMatrix [ 630 0 0 -630 0 630 ] +>> +cairo_image + Gb"0W#BX56H/SW;NF2j8'mgT%fW-Fo?>[`Kjj-sYf3!(&ekM#QR6&W*m:?0DCN%d!log@ng + IYG3j4uA7=i;XaU*TF6OePBG!['c?"A'.-5h*V/*(Vb8Js_u#^@shccMZSk4#tuX3"&?"T0 + Fu@k.*"V<4i84P0\\XFfcXDRn*BF%KI\$/B@.ak%je-g\s&182_86Sn`+j#&qE/[_f3IqRJ + QB=p4oJk'H^?f%/)r$d'kKJ7M,Xi>N%2+K84?\;%j%888hSY+`'"OrB%.CFC`aUdeeO)@?i + pWDQd-L;F!_C(qGdTs+N[)MNl\-nKnE7Ub6o$4@uU;-:>__?*8C'GdjFYQ0h)KnX:j/:aNc + @Gr+3$QuEG=La+&!_@&?(9X@8!0S7SKki(fJ>>YCi>N%2+K84?\;%j%888hSY+`'"OrB%.C + FC`aUdeeO)@?ipWDQd-L;F!_C(qGdTs+N[)MNl\-nG@6NZD5HjR?XK+!WBP(9X@8!3A3NqY + YD=-\hZ%peMMa$4GH^=9-pBfK\hp^-CtdIBsEr[8QqcM$8TeCFC_6CJMT%\#llCDss-__?-t(o^:t7(Uf=,<`2ar\ + ;%j%Yib=YdpabBfVt3M*q75)Z4n(WTs+NuH$e/m(^h9c8MSa+.k)kt>XMp#=EA(eG3jqUa[ + umI$eJ3W!7m[n0dX!>[]Q>broO<75;S"ICFC_6CYm`!3UXbuqkSbq;GRXmY](&*"^oS:gC4 + PfmRiA#/%knM.4e^k@0'JOG3_QV_M4W71$-ceV&oZ\'GdjFYQ1abkH(oN+tG+&*qFD!"bhY + G-nG?[%YP5U/?eHRHr9ruKki(f!':\OXL)![EuqK[TWiAEK`tl)_36B#(A@IVZ9,a%8O6eD + )@?kfNc5Hp(A;!D-,kukMd?:d.4e^k@0,##kkHq!KtYamPnXoJY+`'"lo9*'W@N[^LodCkj + i=7&E".L)@)q6$/f$A3oPR`2;G^K*L;F"rj2d?/Xqp_9A0o3'gFF^$/6kPN!SDDQn7&j7)g + 6DjUn\/:[.n-0=La+&!jL_KTg^":[Z/7k'GM=cTs+O`#iAg3=sna72)1n@5^cDEfRXnr^hM<`2ar\;%j%YiGKt$bS.ZD-JB + Bs1^JkYph5/6X^#JET6Ze151]A?:dFki>N%2^lB**k6Vn^m-S*-$s$8&=La+&!jL_GCNF%p + )tqR;abPp0MhH9,SQVo3fS + s\u.k)kt>XMp#fQ$3d:mHO%;XO/V0]Z-aI_3GWUlAh'GdjFYQ4#L[p#B.d`=La+ + &!jL_WmV22l0:^E"$QE-E]B/?uFsD?\-,k`&n,'@=/6kPN!SHr9Y,Uq<02IYD=(0"8FF(25 + BfE?dCk)IW$470m:fm^@SCnd(@Dd;gSJ4!Q[VT]H^<^\-cY]4tm(gL3+.L-a2&$gV*G8^$N + ]2-8H>MbuD<'nR$470m:fm^@]X)*?]U::uULP`h=H2`WI3lks4jsr(XJE(>%>=sna#i$'a4 + n,qH;oiW%;X:;Y;&NCI?J-AFR0/CW"2;Ql<_VS=qZ`L@+-p'> + BPn]#ZK`tl#d=VrJ2!_a@j&0='l?-^4*c!^?`NN!SXBR&8[h0;g(4ZS/%PBRD%.;kA$Q(Hk%57.e6K&^c%0M'_gS`P$'g + OsL2F0.niX?:,:.k/go$4FY$'5s6P[RU#\VQaL:[oZJ[IT!s#1R]7b8V)5^YQ-46nbRWc6I + kAhSe\^,p.C2NlROCkpLZ?`Z]YA1U9FVK34XO16([@`2_PQn)HfXO;;Z"FZ?\MYh7U+"HC2 + U*-nG?+:;/X>S"7^g"`\CY88dV*q'Y1@0'JLC@;R`' + "-q&n\V_kXJE)u[O`pXOrf;\@);(jdg[30^4-i8CV)X`c&Iu&:%dhKk<,P:=9',,hJH.mnZ + 'd\@j--nG?+hVL+AhXomnn`!-RRp.Blb#h5tKIAAO$dS+/)K0ijof2 + K5hTJpTcYd:>793<>6CXc`5.j9l!Ea='V3X;mL_?+]Ai5l]<=h1>`M!*2'.k)#3$4F + YK6RS0="Jn6%Kak*14fki_\8Z*s.k)):Ka$DaU#X<)C3!Gf'!J)uDQKW^Ho\gtk$%"+^^:D + V^o>5lQC@_L<"B[=UiUFTUr[ms;GG+;krQU?$4FYK_ag%1FQA<#+PoU=696FjB<`CY<`11G + $4FYK,=`Lg7&3`T(QY=[Y,nEYa\%FUUTa`g3c]J-5sDI:Ic=M;X%mjhYeH:[F]MBC(PN%Ne + BpVo)RSP9mN^RVlWdUbEU3I9kub_$Ka$DaZ%FBWXjXo%KBS&O::[0UfSqF4.>!Dn!c[GWJV + =I\jsL%Pn^&"B/V^>[VS + c:=9)):Iq+$q/8jaEiXp9:KC2X58)'"S.k-(&_?+]A`9["(8l;Y$M\j_B/NWQV*bI@:GrmA + 'Pd?Ois!TDuVbZTNB97t]j="gI.k-VpKa$Daak,Yn1uahh.NL]K2[Tr.WOMTG1?_&gc4KH8#e*i4Pd$=fD^[.:VquaQUp/*)>s*:^,#8QM);27bS9 + _33n.Q-:^o:7ZB,]j(7Al#U:6lhL.4uU1e^DNOT6u7-!c[GShAHML#jE&n`eeK\)>7-0m*0 + FHAgN-Q:fm]mS_cNc+9^\Ho82tMr'c%+h8naGQuqrJEp:;g_?+]Am9S3#9gGkQ+J(JuWRUn + 67Q.@-bQ@%I,WQ2>^o=ee9oXAlkNd-I;=g8lSX%5fMM+<)!B>46B6LqO^OX1EUg@[Ll7mH, + HiDQ0NOA)J>EbR*/-)Tnm$JFSfFN0o4D4ZCXOBf6#eQ54]oo+QPnXoJ5k)Fm@rf4<^IXD$D + jbNtF@s(bCcAZ1>:3`1=tEsLA4'c+3-BCkgk@np7u<#FkH=,7elPaS4uJst'GMc=&SD,^/_RE\j" + 5d_?+]!@)u^%_o1RM'Q1%K-t=P,Ka$CM#BEi`9_`Q^`6/lH=*6a>OZaL`8J);HP0Ql*UTa_ + <3I#Ru_/(dR6cGN030BI?QCek1naPn2-<=#@=9*elnAK@"_1"rd&J%nR\ZqE.rC+KcXb`Z + FO0e?HZ\$SYQBU@0&?.pui?CH%&0m&FWTF%lf8WgW%@eGFddY/#h$4FY3UB_kt(uSE*O*I$)$nEe)LVa+3jOf$;#,D'MmOb$ + +`)EFX=C4X>iQs,V;G^Hq,*'G6JAL$Delguq8k,Zp%\e*]12N+02VcYl'*]EJDJ;PP(LOPnCWTro*iW]X + mRg5TnMUK8rBq7F'>#X>F*,-nA<Ip;KJ,`e$^\YP#5NGS$kM)S%n8ghjG-nG?gEq[a9R*U + aQ6bGM&k.9tr2mQj,V\c^p`'`Zk'Gc3DDnRpL?ns\*FRe?9Tc2`3htB["dXs?F)gPYR_?-t + 1eB9WW`cAC\'@0BnC=$7DoOtk!AL.M6"lP,6HY42>jH*+KDb\YpdA[rS0YK;Hu1@X/.O25PoQ7Yg!X%'Gc4eZeHhG4*$8P8F_r&?5ds@rKS(DZQ#p.CF)D + JrVFtrm!dpP(Ok$6LD1MSR<)R[`]J*3oEauITG&nG5^*,& + h#:2-P#4=1`3P8oJ#f_+;WKg/!#r0pWuaM2LN,?H=5mGXW@F5.4bHH%N6(j9FhkV#@^Z91C + Njm]9,8VlEnRb5d$mgs4s@Ab0#Uj$OV&Irs1cA"lR>8P:I%Glm4e_4<0/_ZVE$u,O@lXJ*5 + n(b.t*S#lf=GWd)Y)_m=En`UTo0$4FY+6cYN49\4;J=M,Q.91YtF>@0t,FlN(g`T$R'QVbl + &gUF4r()4FppXV),$c';?a-RZ;T$)GAGVWYLZBE)qgTl948^UeXiC<([MaqFCm>1XTBG;54 + 7>HpU24-K@HD#L6@&?\CbLbe@7^hB;),9ei-,UYT>[On9o%_+RYJpI5-jg?t,tEtH0pk(g] + "`J=@'!4k31bm#*\5=mOR-"3#DR`P4^;)S66C1D6dKu;"LQQneJX#6H&@F)[Z + tg%uq]YcsZELf6tU'!%T6D.4d\4i8W_K/PUCh;Dct-]3`"WoYA\\^1BZ?!VjL9K*r,8:R1? + "Sd*j,`_),_I8c,,;Z(5X`#eAb=",[#l]*!1^gQ`gd(RB0!/m:g>,`Q\2c'62-\k&,fBqkj + V%>Q!AOBl,pqcJZ(Doi;C7QXl"kXR<.2@/jh*U4@STl`9;.j;HD6CL^WoD5(]AKofYQ4UZp + _tQ6XjK;MMZ>]G[#uW_nn4SCC2U8a'S=1'n?!A-cSq4R[CWtp$fDZ,<`7^VJ*3S."+e;jE8 + osXGm9H[4G:[@!;Al^;N.nc/%<:*lA#=ckVrNEI;Zf'9ORr=3;M95K?81-G87:]>iistMP( + 74_I(_YWc_A@d&sKCKi(qP#'+%@@k`N"Ff'BbhuPc;;YX:T*dpHfW7B]k3p#1I!^d]V"^il + ohe[pJe>P1ALHiFKUO-*M8`:cM5M?9(X@i5)dcheMnL6!h#MscI;V<`4/n=&0,%&[(DJ%D#36IrG`&^k#F29ti[9U + /T$D5!0Uph7KBdLkd`lD7?Rr3m6SQ947i\E,*^@K;fUIGAB`SDQKXQ6a7\8WVb:/r6RNZ"l + R2\h'RgjTCWUnVn3\r?:eQo^)g@QLYgCkXSn05_?+]7m9O`#K?_jY`.pHc2/G'(Xab!oFS"%7er@0/JJtN"(tr_Nd%uJkcs.-*BrHP8r>quVEtg + iZ_\59U9FV;33sjtLKoV4SG6KIJ%31TU?u/Cf6(GODdcRH!"QZJI<)t6Y=O]iTBbp2#2LU' + Wj$ra)UhRi=&E'"%\2>jpB*I7ognV8%_l`*W>G1\a1&,DQfo#_F[)e]ZE],#!c[(6r6H'-T + s3aJiC]"$BA*&tP6lpHi./X+/ChZ"7j%XY*6K+";V5+Zi@50B^siC[_HtU,j@:;>XBoNTkc + sln>qOMm?2p4M/JJtNW<5.2%17GZh^:Ybi,=aC'I2@>1EAT(2%V/e!)j+uXtV8?p`;hPoYL + :USeMa'gXQd2=9,3.Slfl,fQ',CIe3X^TWeDi*BLpYTWjdU+SjCiG_!X40Qm0C;M2hek^i< + "&+BRQ"^pWbd3T!N]qPbQO$:>]'Gc4u,=`Lg'Xgp6QQLX+#:M,';b%na9br$p9/O)HT)mG.8!\fo2l5YGnf'?$*d0FoH_?)EVZZ!9<@2j;)q9]FlJAM + @Ap"$i[#)-pB)=jO5n,S.G@-Xk\9a]XVe)a(AhuO/m^so?s1CX]GU/U/D+5d/,$c(8k]e^' + ()q$2+UCZhoK`tlLo^:rJKpA9WJ2\l-!)mh)RMKgJF7f\3\HdA3_?)Fap9(Cb(,fN\Q_/VT + 2^g3W;eL9n?.]3A:4DU3hOq(Q9V$GX45Rgil7qjN&+BdW"^pVd*L#M8Clobj5PG.r:fm^pc + >J]TYik[-q7-`TK>I[DF`uN8GRI`W8'9.@^`*t'J>mTo-cTBC;E[?qmK)1;YQ.A9S(FkPHC + 4Wc"2$p.!>mhYZdPMN(\N4IEt@aEK`tlFi5l]?^KcVX,pAf,.=9(f#+'iVXfIM8ur7hCe6"'d23HAY + e6")9dJ,-V[G_HP6mk8rOj$4FYjnM_4#'_\El.6mP,D + bsO97S^T[hk722\qg4="2$p.!>mieqE0DIf';\oEt@aEK`tlFjD/fG$7 + I:H[n%'/-$om?Ckk-fOg[$5PG.r:fm]e:@4T\:tUk)Oe6uN2^g3W,H(902^iJmKgkR*YQ0W + PhEmig;Kf^, + /^S"hLE>@k$4FYjR2o5c/SpOj/O/k-cVR^D7c)f^3SqWEf;%D^*oInC$c'j:D3Ns2Cp>$15 + P+s%:fm]ec>K0iDdc<2#l1/!G_X':0G8;1_b0T@q4S%HS4S%m@]a'G`t3%NSng:tW + s;OSmH-4@QlhLrJ,h9F?,6pQ?VEJ7m^hChmflC[5R4Ldb=Ka_-I2FdDsP"$AqZ!Q:t:DLKVG1 + COUpUO1,o4hrif(-k"!GHZ9RidUTIRp+WRIcpg(Ts+N+do`rZoZ`e9D/E4L]?(RH^bZZ?5Z + FH%fCBa>DO]i8Y@4n^=8ANpG_X':kgZ_MX3^7(k-1t)l?0!%ciZBt@0,.N9@Ln"%Hp*MQkob7K=rX7( + `&:1qW^;5G>;H'"D)Q]O7)f'o7(VeJ#+L!IW]oG6EpF'SLr'4MW1dg?adY(k]ldnI":t`j"VQbAHR,V5Du&o,p`+tk&dWh0!$@o + =5XOsJO$nO?HW0['T3=KV:fnk8fHUFl&VUC?XLW>'gNt3'fHUEaTs-dieWd6cfZbHD^oo.; + &3q<'OXRrW7j:l\7KL=o@=b)^;7\UQ-2'k0^.2U_i+T2t=*e8OX1,6o4VAY'UBsHk'G^rWp + .,176X^#QX1tq%r+jbd]ldnI"CN=*"al7l&jUtE=MV-*Y-LP!#Vl_ppl@@\,\hIGq%3`T0F + A3_%!s7M"@6Gshe91M:j-r@X1,7uOT<$]@0(jcNs(PX-nHL#4VC"hU9K0@d,sYJMfZA67fl + 4L'G_7s*XNt*;-:VTm#_:2!egfjFY^cF.B8;q2B"Q4J< + (9iZZ&cgcW@0'#)RQ"9=>Bk%dMf!$d":ugm(9i?Q&cgdL_?/#i1i9r5C4Z+TVAZ@OllKL(QZjbp".(IOT9um_?08>;7\T6k@?Mi?.gG'"l,=J/TdQ;*Y-(?TLUn^`sg&jTp#Ye[ + qtYXd]9*l87P+>H_qTHbNEOXOb$"H_7a/9$rFjic=%!Pg,k)o#3_oT:c(gT2#:#%jlbc)e2)-a^H,H%%9Dqc^6HCqtA8?bK*4OnXlnao^I?RnDMM>;mM]?l + 1Z!D1$%qu+8ODbj+%!e.DPlTN#,ikpVn/1_KES=Fe/:lMBmDPCcQhf!*GFqKalpCL2StWG[ + \mYIPgl*a%!4<4^dX/mV-A^h/q[fAu:<4T(#Mjrn5.Q@JK!os0NRqXqq'#QLNNO!PlFf/Sn + G;qYg:5r;"[:4oY5TjidO^*o/tBh(L(6p[mmE5%@'c%tAiF1XA`#B/b4UgYBG5r1@q[M!U8 + FgG-g`[^&C)85b:BfPkg;$V#U8t5!,tes$[_mNWdji2=^V1!kW*^1GO:hK*Bi@;FD0gVNJ%5mD^Oo,[u_,'7HN&@M + (JI<*.X8='t1)UdubkJIe&]hO+$B/@DfZK2pK)bI"se5I\/!Z)"WMIbUG(&p@eI;,6>Rf^+ + \iT>8qeR>4Cmb'3;3uhkN]1FLqU"`f00]kbH&qnK4[jk,o9q(FlJ^Bk)G64BL9@Uds&$DXs + ,P!-*H>Cgl;F?i4C0_[d:FeLOod:^M9aNpZhu@j>?*@EsiOlc&lUHgGA?o"mQCm^4oRhd<& + ^Klj?mFUc+sr)D=[\dc8q5Od`tj6"SM/KLS(5O%D]M\c!_d>oUr4h=&Z?bP4,fctKp4V@b_ + jFb=8PcX`G:UAZV+5!\7^BBi_UITP.iYP+%gGZg?@AA&VXn-.c"=k#a]UrG;Y?Y6-MdK-gh + aSpcf4on[qXhmSo[2VN^,W=$0bM-2*^uke>5e?("RK'ZB + XXT<.Cb4M\rGCfSCtKALfW4WEGA`-LCa+r^XjMs-lA"s1tpWdXR.!:B1?E$aZt[9T[?8j0!"-^B! + OW@*pV>,g[9fqQu?Pkp88T2Y%88ps702+fHXe9RcdK9aUOm#N1Q))Nc5b?l=(Dr8EtQGB<) + rl1DuY>'*2f!S3/`uYNe;7Kl`n/[VAo02rc_7nQ((qpM$&LlrWV*:R%UIor/WFtT%e;E]ab + 5O,lejbN=^*=\p`a@anu#S3q%MR#q8Kb[8PMo^]m@I%0?itFB;.N@,>kTK\+!rK!&: + cbfZ(p*J,J?^p$2hi]ZiK&WMrLl;X0CcDg.gHB[9(aFoOe-Kou+W4^W.cBMdi8^,,343FAk + lBPH1&EI'q=j/WP41.m92JXA7)l!F;:EEh(12Z=T("!+0eDfVYN?s7UB;]\'u`U"&kY>EE3W/lrU40C + l[;#9poOGPQ@JRekZH,L;remQk/BfHX7mqm/coJ2sCA<`cL)a\T34NH4h2_L3ZDo6aZ>^Qs + mZ_Y?U]6e'fWIOuro7BKlZi5HMq\^X[R8,rpM3"&qo])"MG):uKE\m*WDI(MF*WJsMu>rkH + 3m?u&!fcP>1:"ssbqY^?eZRtUH#_b0:'ed\`iSko84?`\TmONcq!_<;G(H*pb`MYmW4Lo5F@[<3In15Majof*cmAHR&Sn'162dMcW(8f + LT(E7\iO<[e+!W&*sg6Nhk6s[qeKom.Q?E9Q+Z$\t9FK^6ZNi4V@_rVUNDJQEL5`f>i"#RV + I-%VFEc0\j#mn+;0rTW=@b2L-$@+mu;6k>OD,*M+`.cZVH_!(]bnLHPH2%V)>E_F`?D)egI + :%Jf&nZnmO?/+,cRC-0p7$k!WKs*,=>0EJ?Uh!!;Jh_H'^?h6Q/AXK6N5,;hXTq:/84pB"r + uM0u%EXKm2YZYt%G'^@Z]m#_8ZG]skqeuar3q*.[Afe_IH(`aCfH3eGq-AF0B*_'O[6f\+? + IGT?md@G,kWaF'n#loh?]u%MQ_S=aYdN9OIo9B*GF:mGcmmTF12H&3h.4OPE7u!:phVJLV* + XMh'j1W<54'@[VaZLkT!I3O\OU(i\.4_rm#'"PF?gZ)>n[V + nV6:1\.SfAQYmJ+oRiS:/e*>M15%5_,-:NfECnIh#':Y9RDhZF$BQg4ZWTs48BkhjB?BktF(G=2+f^WHO%-aU[e7G@Sil$3crqGcP + ']oGnWXL''sA[#T03[sE'2@78):F + igunj.@&kPiksqB&237fn4EF]d:6Omb2W?[NZH\u`p^HMkro4m\lNdLYem@!)8l&V + 9Z_s$kW)TcP4"2WD9,gmu^Iqp.!-"k0_MZZAi//=f,s5+FRf%!/6+k[=&+V].uH(`i/fB8mbg'OrT10T7'A3a + i`uBmA[O2OhSO0,Voc8s*H`2"XX/3P+nQGW+pTTqC2T/S1#oiiM.C6@#Y.F>AUb=P)8V + k5-.))n7El7@iNZY8rr=A..%7%4mYR$OJ.+M*2S=X$:f'c7=D?,h,KgjqtF_#iBr$O'rrVT + =:A1,r'Z9SN9.'*&P>=9R7-,#9qRs*SpNWr&R`V$A.%;-3tTdn3>7#%um + ?W=0Nn8P<'uIRUPi]TO$9Bcft5Q[U_89iH12+,?0rq94RCD/t[;-5)r/P7TlnXcBDSh'jh-nL + P%C),>l8Q!7tMY%Vj+LigNP00Zs^+,V@UAji?dMF?_J>08=2BN6\;oO46;Ul.N8meIrJr%8 + f$%4YEBCU(8B<m'Qu$4Ff3Br)$5(f0TAWp!O4QSnh`'GcMPf9GR#@Q]PqX60GD^:F?dU9HoL5+uFMJ+H7rW + uHR?$4Fd`M2Li!Vl9b_QG1-8#bnOg>dc%%L#9 + \`;!X9^(A(T0;:J,!V!(9Xc^s"hSj6>#[(&tW4Pn(TV'G`C/7B(8V`j99S$G@j9YljJ4Ka! + E`>H+2D`q03o+apTtn/-i_SG9UOnat"&jhI"nh>qkX_?(4l;)+YT^?P`DK>I[jXB%'IYATZ?l2c-^_?*bc5CA*`@QCD: + CB*0_?haU`5[a[t^(fo%MC/m4\,Ie(m/q;HK`s#bq8k]_r3Z+(&&<#r(9m!?V$toe:?%*15iSNBJ/ + ^\L@Q_td=*b,PE!_4%>]RT:jbt1XlM0KOOH:kAi0(8oQ*+RV5PRn93<6i('GbZ665@].ejU + >RZh,XG?laq)h"-b.4]Euqe#QF/VZ?e0-nO;Vbpo$CQE"9eVkI,%GDdfU5bapUJ?-,sqF#i + E/FR^Vhk-qs>gUBgF]M&2aNk(k:/G'.D4a\1SCA8Gi]iA/8%).+*`oKLfBI2D;Z_Ie$4D)d + ZARbF>EmZEfQMspbS<:6;0S-E(BEG]@0$md,GoDIUnjshn#)rKNbk1o4n?2PgT8lck6?H=: + ftLP$WJZijas)u.Vu^p4oC'&(#h#HJBq(p^`203.RV0q?MO'3W0(ro9N7E$>,-H8R4,kK-n + O;E#?<X&H:k0>.dBSs;;6Rag.>gX?3/-BY^jc[X'!ZffCsG + jfQR(:r$=M%@X<18\j7bu\XSJPQbCc`^%"5$"M!@cJs/E^[']?dh#[UXRd?ibrbUcbM`3V + 0.kpV*m!hKtn+b:A,<;-6*1(_t56^4!kl[cJ6d#Q!pZ/J']F$OD&H`H(tC,CO,2gFb.b]V7 + miCaHJS<"p)ZZpgsF.dH9Q/+(jdSfjWV40cF"LlT=RZ9G3r3PYL(Z+8U9MMLkBHmT31-OL7 + &qHcig'\BH+f\F!q7d8@L"#Iosg*)b`!:NEd!HI^2ei;H1$/[<6OmA)4`^Ko=9l3_d8e + GN46<^n4G*5hq]*/Eb]!-aXVs>sn;=MDGVIG"iPZlePZL!Dln6(P\ofB@sGkc-;a7(YQ#hB + N*a;&*r$Yp"1d@5_JJK5OJN4;-:UE5+l@F>0jgmYjGV_2r6)iIdupoF'IbDHF + LnoUTf8EbZ7 + S:f<2)3DQJmFZL>M)HZO&c\V3I*:DL(uh0>S'&g)f2I,YM`QLh<[3\0p@[]?T8;fR+Lrm&X + F@D,n"nd7PoK`jeP!WO"2U7MmEE3MW:h\FjMHY)<8'6EcRqscI>/?_=5Ts0&sXB/7pAnGdX + >[73\F + RcBfND<%)B;2DEIjV17Dmjmmn!5&@TMM]X/jD%@\56;C=#R(W-R + 0ktlIC%.,Pu+leG=94`L'7\TS[U:+t+iji%*\c.TJo#^\ZIh4$oaImh5Rm + <.uAA5Dt[?9PcefX*SW5m!*:S;bC(@\ + 6j/(I?_4b*Ci>k*QgW4?ocNRFZ"#A2\I/C1kZhbdLk\#-+&k6kW6.A"h02:enA?F\Y`B;2D + JDtstPFFq@2^@`Waoo'a89tZk5g6Ph$mkT9c[S.3s+/(qI-nO:\hbp_LJqB9IX`Phq\anNB + K>Qd:EUk=gkQHSCr+rTpH@G`qCCasBdTF-,lo):YB?F"Ii*If_i+IsJeFWIqUVmi1SD!XLY + N`dl;\Z_X$4HX^?`L&;lI7BT4uuc:1XD_u(\BEW.14iFJu0f^"HVpFh4(?+9aeo@0*!iX.l2`T,oYW8E\ + 42+^:rCb7@LD6PL=*\IYa>a3P-N@or3o!e\@UkRmB<'^-*^"ao_W.dH9L/Zk^TUNdt'Wk#d + ZTWl!'rYD%S5>-R>;-:X6Z;f=pkg3f/6oriG[.2pPPIG":F:\1M$J^WX!ra"RK&WF#,[3Bn + cBZ$s_k=4KHD(0e"n"ig!WQB%U7T\\5'W+g\=;1J`:J;>r%&$>7UZ>tqgs]Ok0<3:hFHboY + -'PWc;'QuQQN-@qbUD1s.3(;(nm[q + !s]TupX-OK3l<>uea#p)TaJ0%YWu%'n//cEs*BEE+0?sHRr)!SCV$/9:^,QM=$15?ECo'G^t9;(CkQcX/+B7k8=)TleGZT:^/_c=%Ge'SpC.'l)K]ST:2eNo*W4fjYX!^i;c1joIlUdsNjb1d3RaY*i + @u`EWt@>M8qrOEn9V_,pEpXFosd=O-62,F4oD%gDu7ljgXO5+C6e_\!"d\9<7LO3e)SEjBM + 7HtKa*/^I,?)2Xq<3.SJ&Y6Fct>drHRP8Sl+$O"Ze.dC0FXM8utXU/jigG-fXZJEm@`.p7&ihl/.7U@Mp&2$f@G0%rh=!,sB2[]M_iSG_W8tVlD3YT#9D;J(J4`iDufPL + _"J"flc,ke\"d^+F*V4sR=rcY/`(@Wj7K>HrEKUX$r)D7Ke'"VSYLOW*0CqeEJ`->S(%j]gpaGYZ>'KtRBa,gMMB3m(gYiBT0 + :/?95U(GX76R:Alj3,(>'K)P!sU!Q=H=/gm1]U'sB=Y+hMC*Z?LL7-e$F2s,!6Gq8TcmO:?\+f/>>($[L`Dagkk0/@gl2Jp*0QANU#aH9#356Z*[Er4+m":sjtNtT&'oFgU+L/?*;R,G+;Wj3 + ;:!t("3#'>LJhFftbmO?dO`F*Lgf6tSm5MT;C1G(/EW*!Ksj[rs45l&-O + HKNN?,Mk`\>WOUk?ufT'pPI4G\>huq3aXL:RCgi2bE]pGJb$1*H58*t-f./`gaHHC0KJatp + fW"Tmf?.dC0O4u%7B[4?Q7WkerM%q4u>^^[]_]>M;DDOXa8[DmpP.YS$--&0+O+8X + =qnHc_knu;Eh;m]L7(N0>UZa3o2`;+E[$4@!of2U8knA>S-Cn3ZD]X6JT5"/6QnQ'I8$3(A + bd3BXn[6/)nCTg_F$O"V=#k@IM_$\06'sacl(+Fa*@(^nT"uOMe`&Is"pfZ]j5E + 'Ga4^O/o5BF.XBp*^R7Mn"4JO:9Wj^i_b4XeN1l^?EGiHTWg]NI9o?*?i&2!nH-;hau^+N9 + [mN7;S3CAU0_Kpl1%O>H@*3@AW+_X-nGpWO/f/AM'pU/R!BBS + SONQ07KdQ_:QJn3*SASfdleR+`M?a1mPrW/';0lV^CWM,E.YS$-AV5 + kdIeL+IiW\s>GXra3f#rB$GTbq_293ld$MS)CYZuds!l;?N76Ss]5?g7"*k7[5TYY_%d\Fc + #!Fp8`S($3mgEtdg^=oVh.>7p,A\O%Jp?[&i4F\CXcUkG\gS?XD9*#.>3k[e>Fa+"8rVU+8 + ?_nGP#aD;7p,AXnX(8kV>E-((op7 + qS6L=Zo0c(Pl.H*p>;]7Jisn\Qja'!l6fp76Pd9hL(2JhY!V^#*UrPNt^scic7ulFI:[m*K + qCZY4_]=8n!99l)d!2h7KSALX.d8R;"Qt)g%s%2%ZMeh8S>nM6NJ9Q:nkY.4c#Y>u^!"jN) + 5uiU]t+Sns%)HX6@X%gQPUZE[Zoau`LnrM-S^E-tGM4YpaE'!Imn/tJAF0QJi3@H-Z>/2$0 + 1%eElpjpY%O92J_5i4c4cT%0kHLf_nq2bKDT9\hfoSgW4ehf[c,@9s=b9kgaHr)a#S"bPbf + IplQjV$abhHtJLQ0@Ot_?.QY8OYeaYa"p2MI)]c0Rg4jI + c:sU]eo%3$d0iR]Nd'T@=`r5UnXIDa7=>!HX]ghp28R[1#l)m'3?+Qi?Zttns.KYM%:H'-0'mcNt:iN= + %SWWAg\O;k,EX<'\"PI._W+Wu#*mu9%7ciiPDo+b0G#LVa)nnHfs'4SIDul&d/74BHU5S%KJ?X + g>shK!6`\a'i9^G"l\^[9id-pua%V&.kmBeFUR":6HYI@P/XjAMBFnojI="_"i#-goV7U.V + 4r>FGRd8!ARkK4jQ>W>C**$\X("O?nrP_1#dUQEm6YI:lfU@"+..XOn^H2,8:n!LqtS+3H] + ))oLKQm?a[mi3&g+2n]E,`$[S2X]O!_[gV6ig=!_-nGo40mo(-`f/u)DVa-kNm9?rd:IX&G\E7=XmYU2YIWC8 + 2H6jpJ)T3&Ka!(d,:_u(o[2XGo0c^2q'?5D3a[p]DbUjZ]"M?XSAW5#-s\>%Wp:@Q"bL-eM + L,:$mjCcS'4HkXlc]QU^!7MpRY>]:VV>rd_j!>W\5nZR+Q\1Hq:/kV^EA3lc_K3b9^mQ93f^LckDKi/DtluqXMJ$4HXc3#2,al;od;LP]AC5!@e2FI22'iW2Yf=*6`_GqIg@h;#5#q + umiH$4HX`pQ`m#;:!q%rd!e9YaY+t#1GY8:MA.TiYb?f(7%U0Rs:t`E1c6eY^jJOY+hKg"1 + j.Pikr\f>+.@@gM/L]CNL*W&ga>#SShlH7#B6V96?!,!rfVo76Mahck,G5i\)U]cS1aF'mc + NtN"SoWVj-BW%eEkq1Jdp;AV@o%r\dbF4`#1cT.=Or0Grj5odK>O-(`;+OMe`&Is"pfZYt' + bGE0NArYEDSM)X;\kJK$ne5`Y,)s5C+>s(]8UfqCkb\Q'.P6/$pU-fV1n?F??e\5RB + ;&?KKOqlI&sf7H;";H$4HXZ)AVqhR*eQQLA4j'YrN$K]hAqOO&U' + Ul1>$?f&7'SU'3Ea['5cd3/i)psNC^omHPP$X-d7*M#1\o;@';d)Y/=g-\M:+&[o#og + ;m^qk9&fr1e0(_FOX0$O;/2rdqDWL;F"@e5o(%^\m1H> +Q +Q Q +showpage +%%Trailer +end +%%EOF diff --git a/testfiles/cli_tests/testcases/export-area-page_expected.pdf b/testfiles/cli_tests/testcases/export-area-page_expected.pdf new file mode 100644 index 0000000..4a510aa Binary files /dev/null and b/testfiles/cli_tests/testcases/export-area-page_expected.pdf differ diff --git a/testfiles/cli_tests/testcases/export-area-page_expected.png b/testfiles/cli_tests/testcases/export-area-page_expected.png new file mode 100644 index 0000000..8bdc9e9 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-area-page_expected.png differ diff --git a/testfiles/cli_tests/testcases/export-area-page_expected.ps b/testfiles/cli_tests/testcases/export-area-page_expected.ps new file mode 100644 index 0000000..ce348be --- /dev/null +++ b/testfiles/cli_tests/testcases/export-area-page_expected.ps @@ -0,0 +1,482 @@ +%!PS-Adobe-3.0 +%%Creator: cairo 1.16.0 (https://cairographics.org) +%%CreationDate: Thu Feb 27 23:59:28 2020 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 3 +%%DocumentMedia: 120x105mm 340 298 0 () () +%%BoundingBox: 42 49 291 256 +%%EndComments +%%BeginProlog +/languagelevel where +{ pop languagelevel } { 1 } ifelse +3 lt { /Helvetica findfont 12 scalefont setfont 50 500 moveto + (This print job requires a PostScript Language Level 3 printer.) show + showpage quit } if +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +/cairo_set_page_size { + % Change paper size, but only if different from previous paper size otherwise + % duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size + % so we use the same when checking if the size changes. + /setpagedevice where { + pop currentpagedevice + /PageSize known { + 2 copy + currentpagedevice /PageSize get aload pop + exch 4 1 roll + sub abs 5 gt + 3 1 roll + sub abs 5 gt + or + } { + true + } ifelse + { + 2 array astore + 2 dict begin + /PageSize exch def + /ImagingBBox null def + currentdict end + setpagedevice + } { + pop pop + } ifelse + } { + pop + } ifelse +} def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageMedia: 120x105mm +%%PageBoundingBox: 42 49 291 256 +341 298 cairo_set_page_size +%%EndPageSetup +q 42 49 249 207 rectclip +1 0 0 -1 0 298 cm q +0.254902 0.411765 0.882353 rg +56.691 42.52 m 170.078 42.52 l 177.93 42.52 184.254 48.84 184.254 56.691 + c 184.254 113.387 l 184.254 121.238 177.93 127.559 170.078 127.559 c 56.691 + 127.559 l 48.84 127.559 42.52 121.238 42.52 113.387 c 42.52 56.691 l 42.52 + 48.84 48.84 42.52 56.691 42.52 c h +56.691 42.52 m f +1 0 0 rg +286.188 194.75 m 227.227 191.418 l 191.059 238.078 l 176.008 180.992 l +120.438 161.023 l 170.098 129.078 l 171.922 70.074 l 217.66 107.414 l 274.359 + 90.914 l 252.969 145.938 l h +286.188 194.75 m f +0.501961 0 0.501961 rg +4.251969 w +0 J +0 j +[] 0.0 d +4 M q 1 0 0 1 0 0 cm +286.188 194.75 m 227.227 191.418 l 191.059 238.078 l 176.008 180.992 l +120.438 161.023 l 170.098 129.078 l 171.922 70.074 l 217.66 107.414 l 274.359 + 90.914 l 252.969 145.938 l h +286.188 194.75 m S Q +0 0.501961 0 rg +158.738 195.59 m 158.738 223.77 135.895 246.613 107.715 246.613 c 79.535 + 246.613 56.691 223.77 56.691 195.59 c 56.691 167.41 79.535 144.566 107.715 + 144.566 c 135.895 144.566 158.738 167.41 158.738 195.59 c h +158.738 195.59 m f +0 g +2.834646 w +q 1 0 0 1 0 0 cm +158.738 195.59 m 158.738 223.77 135.895 246.613 107.715 246.613 c 79.535 + 246.613 56.691 223.77 56.691 195.59 c 56.691 167.41 79.535 144.566 107.715 + 144.566 c 135.895 144.566 158.738 167.41 158.738 195.59 c h +158.738 195.59 m S Q +Q q +82 59 151 151 re W n +q +82 59 151 151 re W n +% Fallback Image: x=82 y=59 w=151 h=151 res=300ppi size=1190700 +[ 151.2 0 0 -151.2 82 210.2 ] concat +/cairo_ascii85_file currentfile /ASCII85Decode filter def +/DeviceRGB setcolorspace +<< + /ImageType 1 + /Width 630 + /Height 630 + /Interpolate false + /BitsPerComponent 8 + /Decode [ 0 1 0 1 0 1 ] + /DataSource cairo_ascii85_file /FlateDecode filter + /ImageMatrix [ 630 0 0 -630 0 630 ] +>> +cairo_image + Gb"0W#BX56H/SW;NF2j8'mgT%fW-Fo?>[`Kjj-sYf3!(&ekM#QR6&W*m:?0DCN%d!log@ng + IYG3j4uA7=i;XaU*TF6OePBG!['c?"A'.-5h*V/*(Vb8Js_u#^@shccMZSk4#tuX3"&?"T0 + Fu@k.*"V<4i84P0\\XFfcXDRn*BF%KI\$/B@.ak%je-g\s&182_86Sn`+j#&qE/[_f3IqRJ + QB=p4oJk'H^?f%/)r$d'kKJ7M,Xi>N%2+K84?\;%j%888hSY+`'"OrB%.CFC`aUdeeO)@?i + pWDQd-L;F!_C(qGdTs+N[)MNl\-nKnE7Ub6o$4@uU;-:>__?*8C'GdjFYQ0h)KnX:j/:aNc + @Gr+3$QuEG=La+&!_@&?(9X@8!0S7SKki(fJ>>YCi>N%2+K84?\;%j%888hSY+`'"OrB%.C + FC`aUdeeO)@?ipWDQd-L;F!_C(qGdTs+N[)MNl\-nG@6NZD5HjR?XK+!WBP(9X@8!3A3NqY + YD=-\hZ%peMMa$4GH^=9-pBfK\hp^-CtdIBsEr[8QqcM$8TeCFC_6CJMT%\#llCDss-__?-t(o^:t7(Uf=,<`2ar\ + ;%j%Yib=YdpabBfVt3M*q75)Z4n(WTs+NuH$e/m(^h9c8MSa+.k)kt>XMp#=EA(eG3jqUa[ + umI$eJ3W!7m[n0dX!>[]Q>broO<75;S"ICFC_6CYm`!3UXbuqkSbq;GRXmY](&*"^oS:gC4 + PfmRiA#/%knM.4e^k@0'JOG3_QV_M4W71$-ceV&oZ\'GdjFYQ1abkH(oN+tG+&*qFD!"bhY + G-nG?[%YP5U/?eHRHr9ruKki(f!':\OXL)![EuqK[TWiAEK`tl)_36B#(A@IVZ9,a%8O6eD + )@?kfNc5Hp(A;!D-,kukMd?:d.4e^k@0,##kkHq!KtYamPnXoJY+`'"lo9*'W@N[^LodCkj + i=7&E".L)@)q6$/f$A3oPR`2;G^K*L;F"rj2d?/Xqp_9A0o3'gFF^$/6kPN!SDDQn7&j7)g + 6DjUn\/:[.n-0=La+&!jL_KTg^":[Z/7k'GM=cTs+O`#iAg3=sna72)1n@5^cDEfRXnr^hM<`2ar\;%j%YiGKt$bS.ZD-JB + Bs1^JkYph5/6X^#JET6Ze151]A?:dFki>N%2^lB**k6Vn^m-S*-$s$8&=La+&!jL_GCNF%p + )tqR;abPp0MhH9,SQVo3fS + s\u.k)kt>XMp#fQ$3d:mHO%;XO/V0]Z-aI_3GWUlAh'GdjFYQ4#L[p#B.d`=La+ + &!jL_WmV22l0:^E"$QE-E]B/?uFsD?\-,k`&n,'@=/6kPN!SHr9Y,Uq<02IYD=(0"8FF(25 + BfE?dCk)IW$470m:fm^@SCnd(@Dd;gSJ4!Q[VT]H^<^\-cY]4tm(gL3+.L-a2&$gV*G8^$N + ]2-8H>MbuD<'nR$470m:fm^@]X)*?]U::uULP`h=H2`WI3lks4jsr(XJE(>%>=sna#i$'a4 + n,qH;oiW%;X:;Y;&NCI?J-AFR0/CW"2;Ql<_VS=qZ`L@+-p'> + BPn]#ZK`tl#d=VrJ2!_a@j&0='l?-^4*c!^?`NN!SXBR&8[h0;g(4ZS/%PBRD%.;kA$Q(Hk%57.e6K&^c%0M'_gS`P$'g + OsL2F0.niX?:,:.k/go$4FY$'5s6P[RU#\VQaL:[oZJ[IT!s#1R]7b8V)5^YQ-46nbRWc6I + kAhSe\^,p.C2NlROCkpLZ?`Z]YA1U9FVK34XO16([@`2_PQn)HfXO;;Z"FZ?\MYh7U+"HC2 + U*-nG?+:;/X>S"7^g"`\CY88dV*q'Y1@0'JLC@;R`' + "-q&n\V_kXJE)u[O`pXOrf;\@);(jdg[30^4-i8CV)X`c&Iu&:%dhKk<,P:=9',,hJH.mnZ + 'd\@j--nG?+hVL+AhXomnn`!-RRp.Blb#h5tKIAAO$dS+/)K0ijof2 + K5hTJpTcYd:>793<>6CXc`5.j9l!Ea='V3X;mL_?+]Ai5l]<=h1>`M!*2'.k)#3$4F + YK6RS0="Jn6%Kak*14fki_\8Z*s.k)):Ka$DaU#X<)C3!Gf'!J)uDQKW^Ho\gtk$%"+^^:D + V^o>5lQC@_L<"B[=UiUFTUr[ms;GG+;krQU?$4FYK_ag%1FQA<#+PoU=696FjB<`CY<`11G + $4FYK,=`Lg7&3`T(QY=[Y,nEYa\%FUUTa`g3c]J-5sDI:Ic=M;X%mjhYeH:[F]MBC(PN%Ne + BpVo)RSP9mN^RVlWdUbEU3I9kub_$Ka$DaZ%FBWXjXo%KBS&O::[0UfSqF4.>!Dn!c[GWJV + =I\jsL%Pn^&"B/V^>[VS + c:=9)):Iq+$q/8jaEiXp9:KC2X58)'"S.k-(&_?+]A`9["(8l;Y$M\j_B/NWQV*bI@:GrmA + 'Pd?Ois!TDuVbZTNB97t]j="gI.k-VpKa$Daak,Yn1uahh.NL]K2[Tr.WOMTG1?_&gc4KH8#e*i4Pd$=fD^[.:VquaQUp/*)>s*:^,#8QM);27bS9 + _33n.Q-:^o:7ZB,]j(7Al#U:6lhL.4uU1e^DNOT6u7-!c[GShAHML#jE&n`eeK\)>7-0m*0 + FHAgN-Q:fm]mS_cNc+9^\Ho82tMr'c%+h8naGQuqrJEp:;g_?+]Am9S3#9gGkQ+J(JuWRUn + 67Q.@-bQ@%I,WQ2>^o=ee9oXAlkNd-I;=g8lSX%5fMM+<)!B>46B6LqO^OX1EUg@[Ll7mH, + HiDQ0NOA)J>EbR*/-)Tnm$JFSfFN0o4D4ZCXOBf6#eQ54]oo+QPnXoJ5k)Fm@rf4<^IXD$D + jbNtF@s(bCcAZ1>:3`1=tEsLA4'c+3-BCkgk@np7u<#FkH=,7elPaS4uJst'GMc=&SD,^/_RE\j" + 5d_?+]!@)u^%_o1RM'Q1%K-t=P,Ka$CM#BEi`9_`Q^`6/lH=*6a>OZaL`8J);HP0Ql*UTa_ + <3I#Ru_/(dR6cGN030BI?QCek1naPn2-<=#@=9*elnAK@"_1"rd&J%nR\ZqE.rC+KcXb`Z + FO0e?HZ\$SYQBU@0&?.pui?CH%&0m&FWTF%lf8WgW%@eGFddY/#h$4FY3UB_kt(uSE*O*I$)$nEe)LVa+3jOf$;#,D'MmOb$ + +`)EFX=C4X>iQs,V;G^Hq,*'G6JAL$Delguq8k,Zp%\e*]12N+02VcYl'*]EJDJ;PP(LOPnCWTro*iW]X + mRg5TnMUK8rBq7F'>#X>F*,-nA<Ip;KJ,`e$^\YP#5NGS$kM)S%n8ghjG-nG?gEq[a9R*U + aQ6bGM&k.9tr2mQj,V\c^p`'`Zk'Gc3DDnRpL?ns\*FRe?9Tc2`3htB["dXs?F)gPYR_?-t + 1eB9WW`cAC\'@0BnC=$7DoOtk!AL.M6"lP,6HY42>jH*+KDb\YpdA[rS0YK;Hu1@X/.O25PoQ7Yg!X%'Gc4eZeHhG4*$8P8F_r&?5ds@rKS(DZQ#p.CF)D + JrVFtrm!dpP(Ok$6LD1MSR<)R[`]J*3oEauITG&nG5^*,& + h#:2-P#4=1`3P8oJ#f_+;WKg/!#r0pWuaM2LN,?H=5mGXW@F5.4bHH%N6(j9FhkV#@^Z91C + Njm]9,8VlEnRb5d$mgs4s@Ab0#Uj$OV&Irs1cA"lR>8P:I%Glm4e_4<0/_ZVE$u,O@lXJ*5 + n(b.t*S#lf=GWd)Y)_m=En`UTo0$4FY+6cYN49\4;J=M,Q.91YtF>@0t,FlN(g`T$R'QVbl + &gUF4r()4FppXV),$c';?a-RZ;T$)GAGVWYLZBE)qgTl948^UeXiC<([MaqFCm>1XTBG;54 + 7>HpU24-K@HD#L6@&?\CbLbe@7^hB;),9ei-,UYT>[On9o%_+RYJpI5-jg?t,tEtH0pk(g] + "`J=@'!4k31bm#*\5=mOR-"3#DR`P4^;)S66C1D6dKu;"LQQneJX#6H&@F)[Z + tg%uq]YcsZELf6tU'!%T6D.4d\4i8W_K/PUCh;Dct-]3`"WoYA\\^1BZ?!VjL9K*r,8:R1? + "Sd*j,`_),_I8c,,;Z(5X`#eAb=",[#l]*!1^gQ`gd(RB0!/m:g>,`Q\2c'62-\k&,fBqkj + V%>Q!AOBl,pqcJZ(Doi;C7QXl"kXR<.2@/jh*U4@STl`9;.j;HD6CL^WoD5(]AKofYQ4UZp + _tQ6XjK;MMZ>]G[#uW_nn4SCC2U8a'S=1'n?!A-cSq4R[CWtp$fDZ,<`7^VJ*3S."+e;jE8 + osXGm9H[4G:[@!;Al^;N.nc/%<:*lA#=ckVrNEI;Zf'9ORr=3;M95K?81-G87:]>iistMP( + 74_I(_YWc_A@d&sKCKi(qP#'+%@@k`N"Ff'BbhuPc;;YX:T*dpHfW7B]k3p#1I!^d]V"^il + ohe[pJe>P1ALHiFKUO-*M8`:cM5M?9(X@i5)dcheMnL6!h#MscI;V<`4/n=&0,%&[(DJ%D#36IrG`&^k#F29ti[9U + /T$D5!0Uph7KBdLkd`lD7?Rr3m6SQ947i\E,*^@K;fUIGAB`SDQKXQ6a7\8WVb:/r6RNZ"l + R2\h'RgjTCWUnVn3\r?:eQo^)g@QLYgCkXSn05_?+]7m9O`#K?_jY`.pHc2/G'(Xab!oFS"%7er@0/JJtN"(tr_Nd%uJkcs.-*BrHP8r>quVEtg + iZ_\59U9FV;33sjtLKoV4SG6KIJ%31TU?u/Cf6(GODdcRH!"QZJI<)t6Y=O]iTBbp2#2LU' + Wj$ra)UhRi=&E'"%\2>jpB*I7ognV8%_l`*W>G1\a1&,DQfo#_F[)e]ZE],#!c[(6r6H'-T + s3aJiC]"$BA*&tP6lpHi./X+/ChZ"7j%XY*6K+";V5+Zi@50B^siC[_HtU,j@:;>XBoNTkc + sln>qOMm?2p4M/JJtNW<5.2%17GZh^:Ybi,=aC'I2@>1EAT(2%V/e!)j+uXtV8?p`;hPoYL + :USeMa'gXQd2=9,3.Slfl,fQ',CIe3X^TWeDi*BLpYTWjdU+SjCiG_!X40Qm0C;M2hek^i< + "&+BRQ"^pWbd3T!N]qPbQO$:>]'Gc4u,=`Lg'Xgp6QQLX+#:M,';b%na9br$p9/O)HT)mG.8!\fo2l5YGnf'?$*d0FoH_?)EVZZ!9<@2j;)q9]FlJAM + @Ap"$i[#)-pB)=jO5n,S.G@-Xk\9a]XVe)a(AhuO/m^so?s1CX]GU/U/D+5d/,$c(8k]e^' + ()q$2+UCZhoK`tlLo^:rJKpA9WJ2\l-!)mh)RMKgJF7f\3\HdA3_?)Fap9(Cb(,fN\Q_/VT + 2^g3W;eL9n?.]3A:4DU3hOq(Q9V$GX45Rgil7qjN&+BdW"^pVd*L#M8Clobj5PG.r:fm^pc + >J]TYik[-q7-`TK>I[DF`uN8GRI`W8'9.@^`*t'J>mTo-cTBC;E[?qmK)1;YQ.A9S(FkPHC + 4Wc"2$p.!>mhYZdPMN(\N4IEt@aEK`tlFi5l]?^KcVX,pAf,.=9(f#+'iVXfIM8ur7hCe6"'d23HAY + e6")9dJ,-V[G_HP6mk8rOj$4FYjnM_4#'_\El.6mP,D + bsO97S^T[hk722\qg4="2$p.!>mieqE0DIf';\oEt@aEK`tlFjD/fG$7 + I:H[n%'/-$om?Ckk-fOg[$5PG.r:fm]e:@4T\:tUk)Oe6uN2^g3W,H(902^iJmKgkR*YQ0W + PhEmig;Kf^, + /^S"hLE>@k$4FYjR2o5c/SpOj/O/k-cVR^D7c)f^3SqWEf;%D^*oInC$c'j:D3Ns2Cp>$15 + P+s%:fm]ec>K0iDdc<2#l1/!G_X':0G8;1_b0T@q4S%HS4S%m@]a'G`t3%NSng:tW + s;OSmH-4@QlhLrJ,h9F?,6pQ?VEJ7m^hChmflC[5R4Ldb=Ka_-I2FdDsP"$AqZ!Q:t:DLKVG1 + COUpUO1,o4hrif(-k"!GHZ9RidUTIRp+WRIcpg(Ts+N+do`rZoZ`e9D/E4L]?(RH^bZZ?5Z + FH%fCBa>DO]i8Y@4n^=8ANpG_X':kgZ_MX3^7(k-1t)l?0!%ciZBt@0,.N9@Ln"%Hp*MQkob7K=rX7( + `&:1qW^;5G>;H'"D)Q]O7)f'o7(VeJ#+L!IW]oG6EpF'SLr'4MW1dg?adY(k]ldnI":t`j"VQbAHR,V5Du&o,p`+tk&dWh0!$@o + =5XOsJO$nO?HW0['T3=KV:fnk8fHUFl&VUC?XLW>'gNt3'fHUEaTs-dieWd6cfZbHD^oo.; + &3q<'OXRrW7j:l\7KL=o@=b)^;7\UQ-2'k0^.2U_i+T2t=*e8OX1,6o4VAY'UBsHk'G^rWp + .,176X^#QX1tq%r+jbd]ldnI"CN=*"al7l&jUtE=MV-*Y-LP!#Vl_ppl@@\,\hIGq%3`T0F + A3_%!s7M"@6Gshe91M:j-r@X1,7uOT<$]@0(jcNs(PX-nHL#4VC"hU9K0@d,sYJMfZA67fl + 4L'G_7s*XNt*;-:VTm#_:2!egfjFY^cF.B8;q2B"Q4J< + (9iZZ&cgcW@0'#)RQ"9=>Bk%dMf!$d":ugm(9i?Q&cgdL_?/#i1i9r5C4Z+TVAZ@OllKL(QZjbp".(IOT9um_?08>;7\T6k@?Mi?.gG'"l,=J/TdQ;*Y-(?TLUn^`sg&jTp#Ye[ + qtYXd]9*l87P+>H_qTHbNEOXOb$"H_7a/9$rFjic=%!Pg,k)o#3_oT:c(gT2#:#%jlbc)e2)-a^H,H%%9Dqc^6HCqtA8?bK*4OnXlnao^I?RnDMM>;mM]?l + 1Z!D1$%qu+8ODbj+%!e.DPlTN#,ikpVn/1_KES=Fe/:lMBmDPCcQhf!*GFqKalpCL2StWG[ + \mYIPgl*a%!4<4^dX/mV-A^h/q[fAu:<4T(#Mjrn5.Q@JK!os0NRqXqq'#QLNNO!PlFf/Sn + G;qYg:5r;"[:4oY5TjidO^*o/tBh(L(6p[mmE5%@'c%tAiF1XA`#B/b4UgYBG5r1@q[M!U8 + FgG-g`[^&C)85b:BfPkg;$V#U8t5!,tes$[_mNWdji2=^V1!kW*^1GO:hK*Bi@;FD0gVNJ%5mD^Oo,[u_,'7HN&@M + (JI<*.X8='t1)UdubkJIe&]hO+$B/@DfZK2pK)bI"se5I\/!Z)"WMIbUG(&p@eI;,6>Rf^+ + \iT>8qeR>4Cmb'3;3uhkN]1FLqU"`f00]kbH&qnK4[jk,o9q(FlJ^Bk)G64BL9@Uds&$DXs + ,P!-*H>Cgl;F?i4C0_[d:FeLOod:^M9aNpZhu@j>?*@EsiOlc&lUHgGA?o"mQCm^4oRhd<& + ^Klj?mFUc+sr)D=[\dc8q5Od`tj6"SM/KLS(5O%D]M\c!_d>oUr4h=&Z?bP4,fctKp4V@b_ + jFb=8PcX`G:UAZV+5!\7^BBi_UITP.iYP+%gGZg?@AA&VXn-.c"=k#a]UrG;Y?Y6-MdK-gh + aSpcf4on[qXhmSo[2VN^,W=$0bM-2*^uke>5e?("RK'ZB + XXT<.Cb4M\rGCfSCtKALfW4WEGA`-LCa+r^XjMs-lA"s1tpWdXR.!:B1?E$aZt[9T[?8j0!"-^B! + OW@*pV>,g[9fqQu?Pkp88T2Y%88ps702+fHXe9RcdK9aUOm#N1Q))Nc5b?l=(Dr8EtQGB<) + rl1DuY>'*2f!S3/`uYNe;7Kl`n/[VAo02rc_7nQ((qpM$&LlrWV*:R%UIor/WFtT%e;E]ab + 5O,lejbN=^*=\p`a@anu#S3q%MR#q8Kb[8PMo^]m@I%0?itFB;.N@,>kTK\+!rK!&: + cbfZ(p*J,J?^p$2hi]ZiK&WMrLl;X0CcDg.gHB[9(aFoOe-Kou+W4^W.cBMdi8^,,343FAk + lBPH1&EI'q=j/WP41.m92JXA7)l!F;:EEh(12Z=T("!+0eDfVYN?s7UB;]\'u`U"&kY>EE3W/lrU40C + l[;#9poOGPQ@JRekZH,L;remQk/BfHX7mqm/coJ2sCA<`cL)a\T34NH4h2_L3ZDo6aZ>^Qs + mZ_Y?U]6e'fWIOuro7BKlZi5HMq\^X[R8,rpM3"&qo])"MG):uKE\m*WDI(MF*WJsMu>rkH + 3m?u&!fcP>1:"ssbqY^?eZRtUH#_b0:'ed\`iSko84?`\TmONcq!_<;G(H*pb`MYmW4Lo5F@[<3In15Majof*cmAHR&Sn'162dMcW(8f + LT(E7\iO<[e+!W&*sg6Nhk6s[qeKom.Q?E9Q+Z$\t9FK^6ZNi4V@_rVUNDJQEL5`f>i"#RV + I-%VFEc0\j#mn+;0rTW=@b2L-$@+mu;6k>OD,*M+`.cZVH_!(]bnLHPH2%V)>E_F`?D)egI + :%Jf&nZnmO?/+,cRC-0p7$k!WKs*,=>0EJ?Uh!!;Jh_H'^?h6Q/AXK6N5,;hXTq:/84pB"r + uM0u%EXKm2YZYt%G'^@Z]m#_8ZG]skqeuar3q*.[Afe_IH(`aCfH3eGq-AF0B*_'O[6f\+? + IGT?md@G,kWaF'n#loh?]u%MQ_S=aYdN9OIo9B*GF:mGcmmTF12H&3h.4OPE7u!:phVJLV* + XMh'j1W<54'@[VaZLkT!I3O\OU(i\.4_rm#'"PF?gZ)>n[V + nV6:1\.SfAQYmJ+oRiS:/e*>M15%5_,-:NfECnIh#':Y9RDhZF$BQg4ZWTs48BkhjB?BktF(G=2+f^WHO%-aU[e7G@Sil$3crqGcP + ']oGnWXL''sA[#T03[sE'2@78):F + igunj.@&kPiksqB&237fn4EF]d:6Omb2W?[NZH\u`p^HMkro4m\lNdLYem@!)8l&V + 9Z_s$kW)TcP4"2WD9,gmu^Iqp.!-"k0_MZZAi//=f,s5+FRf%!/6+k[=&+V].uH(`i/fB8mbg'OrT10T7'A3a + i`uBmA[O2OhSO0,Voc8s*H`2"XX/3P+nQGW+pTTqC2T/S1#oiiM.C6@#Y.F>AUb=P)8V + k5-.))n7El7@iNZY8rr=A..%7%4mYR$OJ.+M*2S=X$:f'c7=D?,h,KgjqtF_#iBr$O'rrVT + =:A1,r'Z9SN9.'*&P>=9R7-,#9qRs*SpNWr&R`V$A.%;-3tTdn3>7#%um + ?W=0Nn8P<'uIRUPi]TO$9Bcft5Q[U_89iH12+,?0rq94RCD/t[;-5)r/P7TlnXcBDSh'jh-nL + P%C),>l8Q!7tMY%Vj+LigNP00Zs^+,V@UAji?dMF?_J>08=2BN6\;oO46;Ul.N8meIrJr%8 + f$%4YEBCU(8B<m'Qu$4Ff3Br)$5(f0TAWp!O4QSnh`'GcMPf9GR#@Q]PqX60GD^:F?dU9HoL5+uFMJ+H7rW + uHR?$4Fd`M2Li!Vl9b_QG1-8#bnOg>dc%%L#9 + \`;!X9^(A(T0;:J,!V!(9Xc^s"hSj6>#[(&tW4Pn(TV'G`C/7B(8V`j99S$G@j9YljJ4Ka! + E`>H+2D`q03o+apTtn/-i_SG9UOnat"&jhI"nh>qkX_?(4l;)+YT^?P`DK>I[jXB%'IYATZ?l2c-^_?*bc5CA*`@QCD: + CB*0_?haU`5[a[t^(fo%MC/m4\,Ie(m/q;HK`s#bq8k]_r3Z+(&&<#r(9m!?V$toe:?%*15iSNBJ/ + ^\L@Q_td=*b,PE!_4%>]RT:jbt1XlM0KOOH:kAi0(8oQ*+RV5PRn93<6i('GbZ665@].ejU + >RZh,XG?laq)h"-b.4]Euqe#QF/VZ?e0-nO;Vbpo$CQE"9eVkI,%GDdfU5bapUJ?-,sqF#i + E/FR^Vhk-qs>gUBgF]M&2aNk(k:/G'.D4a\1SCA8Gi]iA/8%).+*`oKLfBI2D;Z_Ie$4D)d + ZARbF>EmZEfQMspbS<:6;0S-E(BEG]@0$md,GoDIUnjshn#)rKNbk1o4n?2PgT8lck6?H=: + ftLP$WJZijas)u.Vu^p4oC'&(#h#HJBq(p^`203.RV0q?MO'3W0(ro9N7E$>,-H8R4,kK-n + O;E#?<X&H:k0>.dBSs;;6Rag.>gX?3/-BY^jc[X'!ZffCsG + jfQR(:r$=M%@X<18\j7bu\XSJPQbCc`^%"5$"M!@cJs/E^[']?dh#[UXRd?ibrbUcbM`3V + 0.kpV*m!hKtn+b:A,<;-6*1(_t56^4!kl[cJ6d#Q!pZ/J']F$OD&H`H(tC,CO,2gFb.b]V7 + miCaHJS<"p)ZZpgsF.dH9Q/+(jdSfjWV40cF"LlT=RZ9G3r3PYL(Z+8U9MMLkBHmT31-OL7 + &qHcig'\BH+f\F!q7d8@L"#Iosg*)b`!:NEd!HI^2ei;H1$/[<6OmA)4`^Ko=9l3_d8e + GN46<^n4G*5hq]*/Eb]!-aXVs>sn;=MDGVIG"iPZlePZL!Dln6(P\ofB@sGkc-;a7(YQ#hB + N*a;&*r$Yp"1d@5_JJK5OJN4;-:UE5+l@F>0jgmYjGV_2r6)iIdupoF'IbDHF + LnoUTf8EbZ7 + S:f<2)3DQJmFZL>M)HZO&c\V3I*:DL(uh0>S'&g)f2I,YM`QLh<[3\0p@[]?T8;fR+Lrm&X + F@D,n"nd7PoK`jeP!WO"2U7MmEE3MW:h\FjMHY)<8'6EcRqscI>/?_=5Ts0&sXB/7pAnGdX + >[73\F + RcBfND<%)B;2DEIjV17Dmjmmn!5&@TMM]X/jD%@\56;C=#R(W-R + 0ktlIC%.,Pu+leG=94`L'7\TS[U:+t+iji%*\c.TJo#^\ZIh4$oaImh5Rm + <.uAA5Dt[?9PcefX*SW5m!*:S;bC(@\ + 6j/(I?_4b*Ci>k*QgW4?ocNRFZ"#A2\I/C1kZhbdLk\#-+&k6kW6.A"h02:enA?F\Y`B;2D + JDtstPFFq@2^@`Waoo'a89tZk5g6Ph$mkT9c[S.3s+/(qI-nO:\hbp_LJqB9IX`Phq\anNB + K>Qd:EUk=gkQHSCr+rTpH@G`qCCasBdTF-,lo):YB?F"Ii*If_i+IsJeFWIqUVmi1SD!XLY + N`dl;\Z_X$4HX^?`L&;lI7BT4uuc:1XD_u(\BEW.14iFJu0f^"HVpFh4(?+9aeo@0*!iX.l2`T,oYW8E\ + 42+^:rCb7@LD6PL=*\IYa>a3P-N@or3o!e\@UkRmB<'^-*^"ao_W.dH9L/Zk^TUNdt'Wk#d + ZTWl!'rYD%S5>-R>;-:X6Z;f=pkg3f/6oriG[.2pPPIG":F:\1M$J^WX!ra"RK&WF#,[3Bn + cBZ$s_k=4KHD(0e"n"ig!WQB%U7T\\5'W+g\=;1J`:J;>r%&$>7UZ>tqgs]Ok0<3:hFHboY + -'PWc;'QuQQN-@qbUD1s.3(;(nm[q + !s]TupX-OK3l<>uea#p)TaJ0%YWu%'n//cEs*BEE+0?sHRr)!SCV$/9:^,QM=$15?ECo'G^t9;(CkQcX/+B7k8=)TleGZT:^/_c=%Ge'SpC.'l)K]ST:2eNo*W4fjYX!^i;c1joIlUdsNjb1d3RaY*i + @u`EWt@>M8qrOEn9V_,pEpXFosd=O-62,F4oD%gDu7ljgXO5+C6e_\!"d\9<7LO3e)SEjBM + 7HtKa*/^I,?)2Xq<3.SJ&Y6Fct>drHRP8Sl+$O"Ze.dC0FXM8utXU/jigG-fXZJEm@`.p7&ihl/.7U@Mp&2$f@G0%rh=!,sB2[]M_iSG_W8tVlD3YT#9D;J(J4`iDufPL + _"J"flc,ke\"d^+F*V4sR=rcY/`(@Wj7K>HrEKUX$r)D7Ke'"VSYLOW*0CqeEJ`->S(%j]gpaGYZ>'KtRBa,gMMB3m(gYiBT0 + :/?95U(GX76R:Alj3,(>'K)P!sU!Q=H=/gm1]U'sB=Y+hMC*Z?LL7-e$F2s,!6Gq8TcmO:?\+f/>>($[L`Dagkk0/@gl2Jp*0QANU#aH9#356Z*[Er4+m":sjtNtT&'oFgU+L/?*;R,G+;Wj3 + ;:!t("3#'>LJhFftbmO?dO`F*Lgf6tSm5MT;C1G(/EW*!Ksj[rs45l&-O + HKNN?,Mk`\>WOUk?ufT'pPI4G\>huq3aXL:RCgi2bE]pGJb$1*H58*t-f./`gaHHC0KJatp + fW"Tmf?.dC0O4u%7B[4?Q7WkerM%q4u>^^[]_]>M;DDOXa8[DmpP.YS$--&0+O+8X + =qnHc_knu;Eh;m]L7(N0>UZa3o2`;+E[$4@!of2U8knA>S-Cn3ZD]X6JT5"/6QnQ'I8$3(A + bd3BXn[6/)nCTg_F$O"V=#k@IM_$\06'sacl(+Fa*@(^nT"uOMe`&Is"pfZ]j5E + 'Ga4^O/o5BF.XBp*^R7Mn"4JO:9Wj^i_b4XeN1l^?EGiHTWg]NI9o?*?i&2!nH-;hau^+N9 + [mN7;S3CAU0_Kpl1%O>H@*3@AW+_X-nGpWO/f/AM'pU/R!BBS + SONQ07KdQ_:QJn3*SASfdleR+`M?a1mPrW/';0lV^CWM,E.YS$-AV5 + kdIeL+IiW\s>GXra3f#rB$GTbq_293ld$MS)CYZuds!l;?N76Ss]5?g7"*k7[5TYY_%d\Fc + #!Fp8`S($3mgEtdg^=oVh.>7p,A\O%Jp?[&i4F\CXcUkG\gS?XD9*#.>3k[e>Fa+"8rVU+8 + ?_nGP#aD;7p,AXnX(8kV>E-((op7 + qS6L=Zo0c(Pl.H*p>;]7Jisn\Qja'!l6fp76Pd9hL(2JhY!V^#*UrPNt^scic7ulFI:[m*K + qCZY4_]=8n!99l)d!2h7KSALX.d8R;"Qt)g%s%2%ZMeh8S>nM6NJ9Q:nkY.4c#Y>u^!"jN) + 5uiU]t+Sns%)HX6@X%gQPUZE[Zoau`LnrM-S^E-tGM4YpaE'!Imn/tJAF0QJi3@H-Z>/2$0 + 1%eElpjpY%O92J_5i4c4cT%0kHLf_nq2bKDT9\hfoSgW4ehf[c,@9s=b9kgaHr)a#S"bPbf + IplQjV$abhHtJLQ0@Ot_?.QY8OYeaYa"p2MI)]c0Rg4jI + c:sU]eo%3$d0iR]Nd'T@=`r5UnXIDa7=>!HX]ghp28R[1#l)m'3?+Qi?Zttns.KYM%:H'-0'mcNt:iN= + %SWWAg\O;k,EX<'\"PI._W+Wu#*mu9%7ciiPDo+b0G#LVa)nnHfs'4SIDul&d/74BHU5S%KJ?X + g>shK!6`\a'i9^G"l\^[9id-pua%V&.kmBeFUR":6HYI@P/XjAMBFnojI="_"i#-goV7U.V + 4r>FGRd8!ARkK4jQ>W>C**$\X("O?nrP_1#dUQEm6YI:lfU@"+..XOn^H2,8:n!LqtS+3H] + ))oLKQm?a[mi3&g+2n]E,`$[S2X]O!_[gV6ig=!_-nGo40mo(-`f/u)DVa-kNm9?rd:IX&G\E7=XmYU2YIWC8 + 2H6jpJ)T3&Ka!(d,:_u(o[2XGo0c^2q'?5D3a[p]DbUjZ]"M?XSAW5#-s\>%Wp:@Q"bL-eM + L,:$mjCcS'4HkXlc]QU^!7MpRY>]:VV>rd_j!>W\5nZR+Q\1Hq:/kV^EA3lc_K3b9^mQ93f^LckDKi/DtluqXMJ$4HXc3#2,al;od;LP]AC5!@e2FI22'iW2Yf=*6`_GqIg@h;#5#q + umiH$4HX`pQ`m#;:!q%rd!e9YaY+t#1GY8:MA.TiYb?f(7%U0Rs:t`E1c6eY^jJOY+hKg"1 + j.Pikr\f>+.@@gM/L]CNL*W&ga>#SShlH7#B6V96?!,!rfVo76Mahck,G5i\)U]cS1aF'mc + NtN"SoWVj-BW%eEkq1Jdp;AV@o%r\dbF4`#1cT.=Or0Grj5odK>O-(`;+OMe`&Is"pfZYt' + bGE0NArYEDSM)X;\kJK$ne5`Y,)s5C+>s(]8UfqCkb\Q'.P6/$pU-fV1n?F??e\5RB + ;&?KKOqlI&sf7H;";H$4HXZ)AVqhR*eQQLA4j'YrN$K]hAqOO&U' + Ul1>$?f&7'SU'3Ea['5cd3/i)psNC^omHPP$X-d7*M#1\o;@';d)Y/=g-\M:+&[o#og + ;m^qk9&fr1e0(_FOX0$O;/2rdqDWL;F"@e5o(%^\m1H> +Q +Q Q +showpage +%%Trailer +%%EOF diff --git a/testfiles/cli_tests/testcases/export-area-page_expected.svg b/testfiles/cli_tests/testcases/export-area-page_expected.svg new file mode 100644 index 0000000..ecdc717 --- /dev/null +++ b/testfiles/cli_tests/testcases/export-area-page_expected.svg @@ -0,0 +1,61 @@ + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/testfiles/cli_tests/testcases/export-area-page_expected.wmf b/testfiles/cli_tests/testcases/export-area-page_expected.wmf new file mode 100644 index 0000000..92421b3 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-area-page_expected.wmf differ diff --git a/testfiles/cli_tests/testcases/export-area-page_export-id.pdf b/testfiles/cli_tests/testcases/export-area-page_export-id.pdf new file mode 100644 index 0000000..c567930 --- /dev/null +++ b/testfiles/cli_tests/testcases/export-area-page_export-id.pdf @@ -0,0 +1,69 @@ +%PDF-1.5 +%”íźû +4 0 obj +<< /Length 5 0 R + /Filter /FlateDecode +>> +stream +xœe=NA …{Ÿâ] ÆöüxÜÒDBąXZDˆ”%ĆBÁőă4hdëéłęnŽ’ÎÒŚï‚ó7 7Ńèÿʗ#*[Kű!ÁSÆ'œŸe‡àDÏX±í9mtÖ1 QÙn0sÎH \uà:•Ž€•Áâ“xg‘–„Ô„kIÔłÙÊlÊ”‘ÄâoL9Ì°Œ&0Í#;T> + >> +>> +endobj +2 0 obj +<< /Type /Page % 1 + /Parent 1 0 R + /MediaBox [ 0 0 340.157471 297.637787 ] + /Contents 4 0 R + /Group << + /Type /Group + /S /Transparency + /I true + /CS /DeviceRGB + >> + /Resources 3 0 R +>> +endobj +1 0 obj +<< /Type /Pages + /Kids [ 2 0 R ] + /Count 1 +>> +endobj +6 0 obj +<< /Producer (cairo 1.16.0 (https://cairographics.org)) + /Creator + /CreationDate (D:20200404202009+02'00) +>> +endobj +7 0 obj +<< /Type /Catalog + /Pages 1 0 R +>> +endobj +xref +0 8 +0000000000 65535 f +0000000615 00000 n +0000000383 00000 n +0000000311 00000 n +0000000015 00000 n +0000000289 00000 n +0000000680 00000 n +0000000971 00000 n +trailer +<< /Size 8 + /Root 7 0 R + /Info 6 0 R +>> +startxref +1023 +%%EOF diff --git a/testfiles/cli_tests/testcases/export-area-page_export-id.png b/testfiles/cli_tests/testcases/export-area-page_export-id.png new file mode 100644 index 0000000..6bf9b04 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-area-page_export-id.png differ diff --git a/testfiles/cli_tests/testcases/export-area-page_export-id.ps b/testfiles/cli_tests/testcases/export-area-page_export-id.ps new file mode 100644 index 0000000..6de42c9 --- /dev/null +++ b/testfiles/cli_tests/testcases/export-area-page_export-id.ps @@ -0,0 +1,130 @@ +%!PS-Adobe-3.0 +%%Creator: cairo 1.16.0 (https://cairographics.org) +%%CreationDate: Sat Apr 04 20:20:10 2020 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%DocumentMedia: 120x105mm 340 298 0 () () +%%BoundingBox: 115 55 291 233 +%%EndComments +%%BeginProlog +/languagelevel where +{ pop languagelevel } { 1 } ifelse +2 lt { /Helvetica findfont 12 scalefont setfont 50 500 moveto + (This print job requires a PostScript Language Level 2 printer.) show + showpage quit } if +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +/cairo_set_page_size { + % Change paper size, but only if different from previous paper size otherwise + % duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size + % so we use the same when checking if the size changes. + /setpagedevice where { + pop currentpagedevice + /PageSize known { + 2 copy + currentpagedevice /PageSize get aload pop + exch 4 1 roll + sub abs 5 gt + 3 1 roll + sub abs 5 gt + or + } { + true + } ifelse + { + 2 array astore + 2 dict begin + /PageSize exch def + /ImagingBBox null def + currentdict end + setpagedevice + } { + pop pop + } ifelse + } { + pop + } ifelse +} def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageMedia: 120x105mm +%%PageBoundingBox: 115 55 291 233 +341 298 cairo_set_page_size +%%EndPageSetup +q 115 55 176 178 rectclip +1 0 0 -1 0 298 cm q +1 0 0 rg +286.188 194.75 m 227.227 191.418 l 191.059 238.078 l 176.008 180.992 l +120.438 161.023 l 170.098 129.078 l 171.922 70.074 l 217.66 107.414 l 274.359 + 90.914 l 252.969 145.938 l h +286.188 194.75 m f +0.501961 0 0.501961 rg +4.251969 w +0 J +0 j +[] 0.0 d +4 M q 1 0 0 1 0 0 cm +286.188 194.75 m 227.227 191.418 l 191.059 238.078 l 176.008 180.992 l +120.438 161.023 l 170.098 129.078 l 171.922 70.074 l 217.66 107.414 l 274.359 + 90.914 l 252.969 145.938 l h +286.188 194.75 m S Q +Q Q +showpage +%%Trailer +%%EOF diff --git a/testfiles/cli_tests/testcases/export-area-page_export-id.svg b/testfiles/cli_tests/testcases/export-area-page_export-id.svg new file mode 100644 index 0000000..9fb1575 --- /dev/null +++ b/testfiles/cli_tests/testcases/export-area-page_export-id.svg @@ -0,0 +1,52 @@ + + + + + + image/svg+xml + + + + + + + + + + diff --git a/testfiles/cli_tests/testcases/export-area-snap_expected.png b/testfiles/cli_tests/testcases/export-area-snap_expected.png new file mode 100644 index 0000000..f35894b Binary files /dev/null and b/testfiles/cli_tests/testcases/export-area-snap_expected.png differ diff --git a/testfiles/cli_tests/testcases/export-area_expected.png b/testfiles/cli_tests/testcases/export-area_expected.png new file mode 100644 index 0000000..c9ded26 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-area_expected.png differ diff --git a/testfiles/cli_tests/testcases/export-background-opacity_expected.eps b/testfiles/cli_tests/testcases/export-background-opacity_expected.eps new file mode 100644 index 0000000..4f5eb82 --- /dev/null +++ b/testfiles/cli_tests/testcases/export-background-opacity_expected.eps @@ -0,0 +1,86 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: cairo 1.15.10 (http://cairographics.org) +%%CreationDate: Sat Apr 11 20:06:29 2020 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%BoundingBox: 0 0 113 75 +%%EndComments +%%BeginProlog +50 dict begin +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageBoundingBox: 0 0 113 75 +%%EndPageSetup +q 0 0 113 75 rectclip +1 0 0 -1 0 75 cm q +1 1 0.5 rg +0 0 112.5 75 re f +1 0 0 rg +0 37.5 37.5 37.5 re f +0 0.501961 0 rg +37.5 0 37.5 37.5 re f +0 0 1 rg +75 37.5 37.5 37.5 re f +Q Q +showpage +%%Trailer +end +%%EOF diff --git a/testfiles/cli_tests/testcases/export-background-opacity_expected.pdf b/testfiles/cli_tests/testcases/export-background-opacity_expected.pdf new file mode 100644 index 0000000..26bdf75 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-background-opacity_expected.pdf differ diff --git a/testfiles/cli_tests/testcases/export-background-opacity_expected.png b/testfiles/cli_tests/testcases/export-background-opacity_expected.png new file mode 100644 index 0000000..9c19536 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-background-opacity_expected.png differ diff --git a/testfiles/cli_tests/testcases/export-background-opacity_expected.ps b/testfiles/cli_tests/testcases/export-background-opacity_expected.ps new file mode 100644 index 0000000..0afec8e --- /dev/null +++ b/testfiles/cli_tests/testcases/export-background-opacity_expected.ps @@ -0,0 +1,123 @@ +%!PS-Adobe-3.0 +%%Creator: cairo 1.15.10 (http://cairographics.org) +%%CreationDate: Sat Apr 11 20:07:25 2020 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%DocumentMedia: 40x26mm 113 75 0 () () +%%BoundingBox: 0 0 113 75 +%%EndComments +%%BeginProlog +/languagelevel where +{ pop languagelevel } { 1 } ifelse +2 lt { /Helvetica findfont 12 scalefont setfont 50 500 moveto + (This print job requires a PostScript Language Level 2 printer.) show + showpage quit } if +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +/cairo_set_page_size { + % Change paper size, but only if different from previous paper size otherwise + % duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size + % so we use the same when checking if the size changes. + /setpagedevice where { + pop currentpagedevice + /PageSize known { + 2 copy + currentpagedevice /PageSize get aload pop + exch 4 1 roll + sub abs 5 gt + 3 1 roll + sub abs 5 gt + or + } { + true + } ifelse + { + 2 array astore + 2 dict begin + /PageSize exch def + /ImagingBBox null def + currentdict end + setpagedevice + } { + pop pop + } ifelse + } { + pop + } ifelse +} def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageMedia: 40x26mm +%%PageBoundingBox: 0 0 113 75 +113 75 cairo_set_page_size +%%EndPageSetup +q 0 0 113 75 rectclip +1 0 0 -1 0 75 cm q +1 1 0.5 rg +0 0 112.5 75 re f +1 0 0 rg +0 37.5 37.5 37.5 re f +0 0.501961 0 rg +37.5 0 37.5 37.5 re f +0 0 1 rg +75 37.5 37.5 37.5 re f +Q Q +showpage +%%Trailer +%%EOF diff --git a/testfiles/cli_tests/testcases/export-background-opacity_expected.svg b/testfiles/cli_tests/testcases/export-background-opacity_expected.svg new file mode 100644 index 0000000..e478c6f --- /dev/null +++ b/testfiles/cli_tests/testcases/export-background-opacity_expected.svg @@ -0,0 +1,54 @@ + + + + + + image/svg+xml + + + + + + + + + + diff --git a/testfiles/cli_tests/testcases/export-background_expected.emf b/testfiles/cli_tests/testcases/export-background_expected.emf new file mode 100644 index 0000000..6263304 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-background_expected.emf differ diff --git a/testfiles/cli_tests/testcases/export-background_expected.eps b/testfiles/cli_tests/testcases/export-background_expected.eps new file mode 100644 index 0000000..f58830e --- /dev/null +++ b/testfiles/cli_tests/testcases/export-background_expected.eps @@ -0,0 +1,86 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: cairo 1.15.10 (http://cairographics.org) +%%CreationDate: Sat Apr 11 19:29:54 2020 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%BoundingBox: 0 0 113 75 +%%EndComments +%%BeginProlog +50 dict begin +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageBoundingBox: 0 0 113 75 +%%EndPageSetup +q 0 0 113 75 rectclip +1 0 0 -1 0 75 cm q +1 1 0 rg +0 0 112.5 75 re f +1 0 0 rg +0 37.5 37.5 37.5 re f +0 0.501961 0 rg +37.5 0 37.5 37.5 re f +0 0 1 rg +75 37.5 37.5 37.5 re f +Q Q +showpage +%%Trailer +end +%%EOF diff --git a/testfiles/cli_tests/testcases/export-background_expected.pdf b/testfiles/cli_tests/testcases/export-background_expected.pdf new file mode 100644 index 0000000..a5887b7 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-background_expected.pdf differ diff --git a/testfiles/cli_tests/testcases/export-background_expected.png b/testfiles/cli_tests/testcases/export-background_expected.png new file mode 100644 index 0000000..1a3a34f Binary files /dev/null and b/testfiles/cli_tests/testcases/export-background_expected.png differ diff --git a/testfiles/cli_tests/testcases/export-background_expected.ps b/testfiles/cli_tests/testcases/export-background_expected.ps new file mode 100644 index 0000000..5055f6b --- /dev/null +++ b/testfiles/cli_tests/testcases/export-background_expected.ps @@ -0,0 +1,123 @@ +%!PS-Adobe-3.0 +%%Creator: cairo 1.15.10 (http://cairographics.org) +%%CreationDate: Sat Apr 11 19:30:09 2020 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%DocumentMedia: 40x26mm 113 75 0 () () +%%BoundingBox: 0 0 113 75 +%%EndComments +%%BeginProlog +/languagelevel where +{ pop languagelevel } { 1 } ifelse +2 lt { /Helvetica findfont 12 scalefont setfont 50 500 moveto + (This print job requires a PostScript Language Level 2 printer.) show + showpage quit } if +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +/cairo_set_page_size { + % Change paper size, but only if different from previous paper size otherwise + % duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size + % so we use the same when checking if the size changes. + /setpagedevice where { + pop currentpagedevice + /PageSize known { + 2 copy + currentpagedevice /PageSize get aload pop + exch 4 1 roll + sub abs 5 gt + 3 1 roll + sub abs 5 gt + or + } { + true + } ifelse + { + 2 array astore + 2 dict begin + /PageSize exch def + /ImagingBBox null def + currentdict end + setpagedevice + } { + pop pop + } ifelse + } { + pop + } ifelse +} def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageMedia: 40x26mm +%%PageBoundingBox: 0 0 113 75 +113 75 cairo_set_page_size +%%EndPageSetup +q 0 0 113 75 rectclip +1 0 0 -1 0 75 cm q +1 1 0 rg +0 0 112.5 75 re f +1 0 0 rg +0 37.5 37.5 37.5 re f +0 0.501961 0 rg +37.5 0 37.5 37.5 re f +0 0 1 rg +75 37.5 37.5 37.5 re f +Q Q +showpage +%%Trailer +%%EOF diff --git a/testfiles/cli_tests/testcases/export-background_expected.svg b/testfiles/cli_tests/testcases/export-background_expected.svg new file mode 100644 index 0000000..7e49fab --- /dev/null +++ b/testfiles/cli_tests/testcases/export-background_expected.svg @@ -0,0 +1,52 @@ + + + + + + image/svg+xml + + + + + + + + + + diff --git a/testfiles/cli_tests/testcases/export-background_expected.wmf b/testfiles/cli_tests/testcases/export-background_expected.wmf new file mode 100644 index 0000000..3a6d0f4 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-background_expected.wmf differ diff --git a/testfiles/cli_tests/testcases/export-clone-group-filtered-mp.svg b/testfiles/cli_tests/testcases/export-clone-group-filtered-mp.svg new file mode 100644 index 0000000..1efdb42 --- /dev/null +++ b/testfiles/cli_tests/testcases/export-clone-group-filtered-mp.svg @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + Test if rasterization of filter effects is trigerred when rendeding + a clone of a group containing a filtered element. + + + + + + + diff --git a/testfiles/cli_tests/testcases/export-clone-group-filtered-mp_expected.png b/testfiles/cli_tests/testcases/export-clone-group-filtered-mp_expected.png new file mode 100644 index 0000000..e862a55 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-clone-group-filtered-mp_expected.png differ diff --git a/testfiles/cli_tests/testcases/export-default-background_expected.eps b/testfiles/cli_tests/testcases/export-default-background_expected.eps new file mode 100644 index 0000000..6aca4538 --- /dev/null +++ b/testfiles/cli_tests/testcases/export-default-background_expected.eps @@ -0,0 +1,86 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: cairo 1.15.10 (http://cairographics.org) +%%CreationDate: Sat Apr 11 19:05:42 2020 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%BoundingBox: 0 0 113 75 +%%EndComments +%%BeginProlog +50 dict begin +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageBoundingBox: 0 0 113 75 +%%EndPageSetup +q 0 0 113 75 rectclip +1 0 0 -1 0 75 cm q +1 0.814948 0.847259 rg +0 0 112.5 75 re f +1 0 0 rg +0 37.5 37.5 37.5 re f +0 0.501961 0 rg +37.5 0 37.5 37.5 re f +0 0 1 rg +75 37.5 37.5 37.5 re f +Q Q +showpage +%%Trailer +end +%%EOF diff --git a/testfiles/cli_tests/testcases/export-default-background_expected.pdf b/testfiles/cli_tests/testcases/export-default-background_expected.pdf new file mode 100644 index 0000000..720134d --- /dev/null +++ b/testfiles/cli_tests/testcases/export-default-background_expected.pdf @@ -0,0 +1,70 @@ +%PDF-1.5 +%”íźû +4 0 obj +<< /Length 5 0 R + /Filter /FlateDecode +>> +stream +xœmÌÁ €0 Đ{§ÈÖ€šÆŽá"ړëț`’ŒÈ‡Oà?B€šŽ„ażÂôŽÂ©Ì~”ŒČ@«0nő ƉRdóí€ÓŒÆu1‰Î_čR©dêŰüQ@6 ÿ~YĂ Žđ$Í +endstream +endobj +5 0 obj + 102 +endobj +3 0 obj +<< + /ExtGState << + /a0 << /CA 0.74902 /ca 0.74902 >> + /a1 << /CA 1 /ca 1 >> + >> +>> +endobj +2 0 obj +<< /Type /Page % 1 + /Parent 1 0 R + /MediaBox [ 0 0 112.5 75 ] + /Contents 4 0 R + /Group << + /Type /Group + /S /Transparency + /I true + /CS /DeviceRGB + >> + /Resources 3 0 R +>> +endobj +1 0 obj +<< /Type /Pages + /Kids [ 2 0 R ] + /Count 1 +>> +endobj +6 0 obj +<< /Producer (cairo 1.15.10 (http://cairographics.org)) + /Creator + /CreationDate (D:20200411190529+02'00) +>> +endobj +7 0 obj +<< /Type /Catalog + /Pages 1 0 R +>> +endobj +xref +0 8 +0000000000 65535 f +0000000547 00000 n +0000000328 00000 n +0000000216 00000 n +0000000015 00000 n +0000000194 00000 n +0000000612 00000 n +0000000899 00000 n +trailer +<< /Size 8 + /Root 7 0 R + /Info 6 0 R +>> +startxref +951 +%%EOF diff --git a/testfiles/cli_tests/testcases/export-default-background_expected.png b/testfiles/cli_tests/testcases/export-default-background_expected.png new file mode 100644 index 0000000..bd530a8 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-default-background_expected.png differ diff --git a/testfiles/cli_tests/testcases/export-default-background_expected.ps b/testfiles/cli_tests/testcases/export-default-background_expected.ps new file mode 100644 index 0000000..2ea710b --- /dev/null +++ b/testfiles/cli_tests/testcases/export-default-background_expected.ps @@ -0,0 +1,123 @@ +%!PS-Adobe-3.0 +%%Creator: cairo 1.15.10 (http://cairographics.org) +%%CreationDate: Sat Apr 11 19:05:53 2020 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%DocumentMedia: 40x26mm 113 75 0 () () +%%BoundingBox: 0 0 113 75 +%%EndComments +%%BeginProlog +/languagelevel where +{ pop languagelevel } { 1 } ifelse +2 lt { /Helvetica findfont 12 scalefont setfont 50 500 moveto + (This print job requires a PostScript Language Level 2 printer.) show + showpage quit } if +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +/cairo_set_page_size { + % Change paper size, but only if different from previous paper size otherwise + % duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size + % so we use the same when checking if the size changes. + /setpagedevice where { + pop currentpagedevice + /PageSize known { + 2 copy + currentpagedevice /PageSize get aload pop + exch 4 1 roll + sub abs 5 gt + 3 1 roll + sub abs 5 gt + or + } { + true + } ifelse + { + 2 array astore + 2 dict begin + /PageSize exch def + /ImagingBBox null def + currentdict end + setpagedevice + } { + pop pop + } ifelse + } { + pop + } ifelse +} def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageMedia: 40x26mm +%%PageBoundingBox: 0 0 113 75 +113 75 cairo_set_page_size +%%EndPageSetup +q 0 0 113 75 rectclip +1 0 0 -1 0 75 cm q +1 0.814948 0.847259 rg +0 0 112.5 75 re f +1 0 0 rg +0 37.5 37.5 37.5 re f +0 0.501961 0 rg +37.5 0 37.5 37.5 re f +0 0 1 rg +75 37.5 37.5 37.5 re f +Q Q +showpage +%%Trailer +%%EOF diff --git a/testfiles/cli_tests/testcases/export-default-background_expected.svg b/testfiles/cli_tests/testcases/export-default-background_expected.svg new file mode 100644 index 0000000..9f328c2 --- /dev/null +++ b/testfiles/cli_tests/testcases/export-default-background_expected.svg @@ -0,0 +1,54 @@ + + + + + + image/svg+xml + + + + + + + + + + diff --git a/testfiles/cli_tests/testcases/export-dpi_expected.eps b/testfiles/cli_tests/testcases/export-dpi_expected.eps new file mode 100644 index 0000000..0b21574 --- /dev/null +++ b/testfiles/cli_tests/testcases/export-dpi_expected.eps @@ -0,0 +1,282 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: cairo 1.16.0 (https://cairographics.org) +%%CreationDate: Tue Mar 28 19:36:46 2023 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 3 +%%BoundingBox: 0 1 173 91 +%%EndComments +%%BeginProlog +50 dict begin +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageBoundingBox: 0 1 173 91 +%%EndPageSetup +q 0 1 173 90 rectclip +1 0 0 -1 0 91 cm q +0.9 0.950196 0.9 rg +90 45 m 90 69.852 69.852 90 45 90 c 20.148 90 0 69.852 0 45 c 0 20.148 +20.148 0 45 0 c 69.852 0 90 20.148 90 45 c h +90 45 m f +0 0.501961 0 rg +82.5 45 m 82.5 65.711 65.711 82.5 45 82.5 c 24.289 82.5 7.5 65.711 7.5 +45 c 7.5 24.289 24.289 7.5 45 7.5 c 65.711 7.5 82.5 24.289 82.5 45 c h +82.5 45 m f +Q q +81 0 92 90 re W n +q +81 0 92 90 re W n +% Fallback Image: x=81 y=0 w=92 h=90 res=300ppi size=432000 +[ 92.16 0 0 -90 81 90 ] concat +/cairo_ascii85_file currentfile /ASCII85Decode filter def +/DeviceRGB setcolorspace +<< + /ImageType 1 + /Width 384 + /Height 375 + /Interpolate false + /BitsPerComponent 8 + /Decode [ 0 1 0 1 0 1 ] + /DataSource cairo_ascii85_file /FlateDecode filter + /ImageMatrix [ 384 0 0 -375 0 375 ] +>> +cairo_image + Gb"0WGB=Pn\c6Z%%44*ud^u(.KMYC`,R3VR5&gnFp9BgFm_na4)sko\Vu^)(,`76nLgk[36 + P9Z$5sk2j#t_(uHIo;7Valj8d58Y[Ur..*%#:"!A&,^uC(f:Bbi",b%L`6$jJ)Ci,eA*1$"+N#!#6)C[pk'/9 + n-As.+,m.L5O\pW?pkJ%JNrW/,AQ-Ir..'L;$2=+&,a8")YaDai"&<,^`W=RIk[T$rkKWNK + +U!1#QDL\N2V:ln-Ar#6k0'F5O\n&C>]X#JNjuK"+T5>r5"1-p]H$(%n>'U4DH$&RNFCCUJ + %AZ^8AogM?k[i'UuSi@73U.K$8?#o!iE*$?QHqo!S9>L6B]jb)4m[rHF^qM'-E9hue + JqHlgBaNQmIF;jpcTF$ir5(&.P[(I))PMnUD&CZP%VDpgH?1"I;M.^SQ0ga@]@*DJT+pW + d(H6ur1nmLQrN89Fe,r4i8[:3q#8AWb/"$2S15cn&56Zf["d>o:/7g&-s-KIZ5[8l0N]'#3 + OMl=n],;U-T1?-L#Rj]:QQ)Bu^Y>p9$md*r&FCi6>sTBrTEUs^Mkr7Q;GCl%/d^A%;l4*t# + "?V,ce=rF_gH/ruIA9RHYB!d@t'XihNK$X\e0!01jFEb@V`lU*Soh='WVE_1s[$otmQ9VM; + +8B?("+T5>r..),#6)C[p]H"6%K(cAn-ArK)YaDai",_t2=LkM^`W=rC>]X#JNrWoeA*1$" + +T6iW.@2&#QDL\;$2=+&,^uCU'CV4+8>qf6k0'F5O\pWL`6$jJ)Ck8&N0%_rkKWN,AQ-Ir. + .*%8(>r"1flpd> + )Ic%%Vf3LTX!WA8891c*#qB*OhhfX8cI:pDpYVr!QCbh#e54l!#M5S[q")B4l-](# + Kc(o,Y-G.@[TZn5j]#`i8M*TK;/kPrd4JCQ[g4(j)h%gs!i1#r^ur9>/R:Q$pmfg=aCS#i0 + )>:d`ugr9Flq^[Jj,SA4EkZKX4]eF=%'1CFEs'kS22P5=N;Z.7j9`6__]/tAQ38TW\q?;\X + Wp[;klp=bq#8%6sl1e1%MDMRN&X,>$Zc@PG-BC^[utWH<4*S=O*;/(4$2H*d_65K>4%5?:qb + +n2PuX,.roqF:G>P.SPE_*ej4aGe1Tk?%TGpn.P/l>P*4Bl:$@fO8+(L3jGtT%@pBK)(!uo + jQEUe[t^EmehC@p?:bpPFJRbJ+rpqGePla@kafH*ePg7:7/J:g=4qW3PE#ieb,9[X5[,`/o + Qm-k0'5W'0T:;;[F"1F9Gc>_4UF[>jJ"sP77HG/%la^(39--=\e-8c`K=Eo$8]_9M6L?j*\ + n0+X=WiE2"TL;tlJea[(%<,r..),#6)C[p]H"6%K(cA + n-ArK)YaDai",_t2=LkM^`W=rC>]X#JNrWoeA*1$"+T6iW.@2&#QDL\;$2=+&,^uCU'CV4+ + 8>qf6k0'F5O\pWL`6$jJ)Ck8&N0%_rkKWN,AQ-Ir..*%8(>>=%07fbK7Nsj7GR3GKS]c^[]4T=T4973Kf + Dq4Fa:1mK7"iB9N:jf)fe10)h(<159 + sO^L,+DOsBZE@1>e-X&efO'M[>@t1-t&@4)T@tsCg3m;@V-TZj)"!'=YBKl%dk:fP\ba0XQT,eZ==;3N`]B8LF;$M"^0N)!q^(tm.l0a-r + ]Z';]8s;'dE"OCbG1BW``d!H.VWuCHKr&&I=Y=e-B?B&h];XN#=gu9o\n1.l)LX#FC0/tAm + Lt>53qk-Z]BPAjIU-C7gNaZ7@#qVU + oue/.8tjDi>mk#U&6df!I;709*>mn^4&\?VlmGn>9!Bs+?M.O$\.I+ER@5/X[rnMi",a:5=5;4cmKIb&,^uCn4-f7b)Bm7 + $iGQ?n-AsjnGZ6Aoel4DrkKWN,K(\(4Wao\5O\pW^`Q5Ee"/4(^`W=RJ)HA6r1QXcp]H$(& + ,[%!:RRoXm=-eeJNrW/r`I76Vg^96rkKWN"+Mlb:Plf]NVDGk&,^uCnAfSK'Y!m">2^"iJN + rYEUAhOi?G)6ommLbW7V,)0MUfX+`&PDXi9cqfB6&=unG>Rs%#$q>k0G](r*kmLf:N@u'Bg + ]`NcjLN,IZRZ#2^B6o'F+iBE"_C[dHq6?1jEZ0f0`+NCH[?-:2FS`tNo]U\6(Q3m+;bO#R%4Q%2r5"AG5h96q9M-[p=46b4M7:-HkdGf + oPl"kVr^2:NS42)]OM$=s!3Brq6IrmWgTOg.:cVgMd!E*Db4TNUkRk&X^HG0.,,Bg&osi,= + lUkL)/H1>JOV?W3p%d5m-jiq/c4F*-T>8$GSaP:$-#PT29LhE1YlRFF#?+.VD62#6@cJua! + U1@`b]oYUP]07fSW+6-.n%/ph2jNiG)"e8cpTr/qY55j"0Sp,JZ)*&`LKO7ZVrQ3uYdrRb- + oHRPQO?]8Tq2Th%aSS8;61F(7<>[7E&,]:>nAi]MU$,VlrkKUE#(N`3TC4Fai",`_;Xha'r + V?F*#l_U],mAd7_Op;R#_PA27nLN=S+)2JBPN&^n4-p'+'\95]!`OV^`S76Ir*)rU7.gRjs + Kj*J!:ZFYn_be)H.+CJNla6rn*;s`APX,#R5e:O(*]*2WPB[CbW^JpnO57O)=\kAgi9rn4, + 4L+5?@aW2<*eVh75'7iB,bS+EOG?NP`8O"u;O2WmkRFmd/)L[YQJ-m8:pF8k6"jWp*:_&rH + IJDcJ?qK'@.3rFOn$4-X`U-FK[M7^_cW'=ATY&\t!4o@Us + &H&e1J#JU;'UCst+gZf50+;(+Bkd@jl$K;Ni/\iTZ,^DHa)BtuJB;&G%\*DWQ$S>:Z/fDPf + mi2Tr`&&+BG.K/?+F*[1SZ]m=rOMt?L!$3ro`(p\XXFi/t[>/OY5,pf:qR5h,"+TYg=o + @OHdVZYcGZk/Cn=(Q20g9)Z*hGq2Qt%Sc3$#S[kXti>]#Hd7^i6;.eMK*?aQnmVVod?KE2" + R&QR*_F3c[(A.!706h"f1c&rOqnb9ZA/6=Qg[^JBbD:V"Ap?, + +[hEFU3!%fa))m^20,553)2lg5kfhO!mke?)>leKU=6Xt)fB%(J-PT_c=h-aE;bfsV-RLcabN1=N]LUAdPnX']_P>S@M%A-8eW]p)"jCW&&r'>KCr36HU3qP$er'?2Hr`Fe;a-)M"+(U/C + +)CA$MTCU(5KM]55OJ(j[63!6rj;\q5k#"--MZ%bJ+haTCJfRV5<'+%OM(gIg?HquK)T=YI + uYluLHil:Jj8Ypk9jJ`@SqX/s!])cK+o!sBr=2A3'J"H)f8H%_U??[&$i7o:](*1N-/\-P? + aE$39fprr+Y=1_.%HC%oPr=q@g^nqelESqGVDt>^HnR)ZG$#k=a,RjYT24I_a3Y&q*?C2NO%Jp>;R + qWh6k0C3ci?d`^)Qo+O('D-"WV*h-AcS)6rTV\XM=E,!8=6(W^`lkmV(V&_5^;"Yr4Z#;'( + 'qIJ54$VWIV&QiGMDS)-'rb2UF2"p + 4nqdo^qdO]WZ\Zh!f$i?+4u/5.c'4kJ3^-s!aihgP9lpm1+W-^fp\F7fN%$lhA\UGMYOQ2Q + ,1$^:kb:"$NYQg5CYcf2iIO=p$,TQKBsu\dij31G`T)-59>Y&n-@WBn>AYQFEZqs^$rVpr,GAKK0RG + +Jj1:HLY?rrTB6!EG$a&4Iup3(Ilcl@g.Q35lAsOS0DL1d,l#hcJ,X]jNu%D$qpk*'#MD]< + Vka6Mo,hUBIlnB-i=E5Xi(llAl/pDGV(BT+r]Kj*0_g87'E%*n5?)0*UuhMO-`Gu7ZBO4-" + Fmjeh/56&<:MI5KBslY,Jg"KiCbcccVr_QLHe5>B8>9rj;V9+S^R''E"!N%i(ZJDE3 + 4#rWj)56&H!J8FpKGm9Q%LVsTNF_&oJ?i/]^E2F_VlS3Llj((U+l'GLD,k0MhE\X)ibm>iC + ,+/G:^O#2@cE;U64o>``F(&f>&#l[BdkKWsD>0`bK56=RY/.#YbIuj_`5LTIln.EV-p>=OC + \,!"K?1T7l*h.HTST&lJhgn!_CbkkYc@L2rqYDd=ea=R+=P`]5M9\h + l)e_RELRc$-@:R+GD*$qT"'_Tb?o//Efklr'>Kfsu$iEa<#ptdejE]'``aA%q5`mV"q@g`_O_QitUYZIulerJ#)m-%_s1]K:'IsC&Y%EeaNhC+ + D1+5RQmC#T(!!a+D1+5RQpTC&,\`ki/]^Er,;C8d%=G8KBsfW,L)joGPC+qGPHfX_&mCj*5 + $dJErP"gccAk55LTHirnod6JNnr.r/p+P^Ae[m&3oq7c(FLLE6J'0huA>q,krV/TD6'Dp]H + !W1\`32T?b`Qn!2b9TKi5MccA;%58*nYWTr.[HogO'p:ik9P$J"=ec$@P[L\MY&,-#ikWb5 + MrM/HP^]*s\L'Yl?f_6-Nm3a0,,cs6/**QLi-ShXsJ8[/.-( + X:4D:pk+5)J#)lr-Y/8!r)(G6i"*,Ki/]^EejlpQS3LltDX[na6h!U75CPQi/E`'T5n/=*4 + Tp81q%K2$3#T@%'uHgDXhRlp^mEI,mAcZl;(LEY">H]QLi-Ad0'@j3skn.e=Y, + PUF"jIIXg)Nn.5F_8dbQ>e=Ve^GPIZr*V-,q&-'EeUnlpE8,ZR*[E>6CG[9om5KHTO599DE + n4oWFrFt:NO22p6Yi&<4r1TH-YoqG7rj;Wn2rG3E=R`4UrX@U;<#RsfoV_(p^;D%gYi&<4r + ;"fu5?ZL.X]N!K+(RUPT+:\'-%:b@rRlXB48_D<'E%*V5NafJ1&]c>[n6-L:KoXD^E\Lu6a + dNimb/]\+G%YucT;Bg@JXKaqsQL8r3\chj%oK[l^t]3Wd*'(F%^KD]BT(>!cJ;rNEA6_EtA + Ah$0rZY]N0Nn\THk"rq':dT5eNK/V,mpZ2Y8g(l)@5DL,kTVppT'9<3TUgjuS&Pqdlc6YenPkV-H^>h4JoURtgI:-^.cMS,"4#qg6,UGMMHE2AdEU,uA + 9!3qbNtL4nQChC\=i=sC(5=ErI=$1;Tep,CcRDYVVm"^qma"t^?SVLN/k\BuFSJDTOiJ6%) + FVnq:G!nP`q?Y'B^a=^Hm`m%j>Ue7q]PZd"@f:2iPVapWoqHo,JQA8jg)s=Sm5`3l-E^4rmdbB*)AcT*dllV;H!"^Sc8EAoXCb`V]VF`5KM]55OJ(jW?fT[re.)&^`R* + *_4PoIPH2s9W'B5O_r2-3\)<<)piA:VBKPa85=eA^+)CB/e:U]h#MEPT`u=kirssV;%oQ

@2a:;W.re.,'_&m3[_&or]*)/I4RF/1@I + DI#$]\niQIld5I48_AO$iYtTVaKpc_]P\Yi/^ui3qU]Zr':YtrWr/Z7.Q1piWF[lP(Gn"r< + Z4,L-NbNK0KG9OmqiDIuohF5GJ(mD$Qiq-s'VgIn#-%?9pKq.^VnumNM + FbS%sC5R&3EtU;9=7WNL.(k[>R3=/A`(&GP"c*iC#%Y=6Ha43 + +*h.qhitVjRK,t-S[9:8hNi`WlgJ?ko-!33[]0tU9fb;OnR8u9^h6"PBloekLT*IQfjul5B`>Drqp + HYO!a]tS/a5.P5cfLNUn1b_3#6pdR2+_jAO+NuJ#MA$Ypj5EURoX$?%pI\/5"=or\)SoXa4_6pk/3[R%t"L;WhmM+Q@ + N">hP8;1I"DdbkZ6Z1[%9/c'N?ru)+apk,Xnpg]6]gb62KJ)H@hJj6^-:O7:Ir..'># + l`JH5K,M!R?a30#Q?+opg\CEBPY$F#Q?+rpg[P-PT!0YY6KL7O"u;O[cJ%uLVY)-UR9u#%J + `qu.r'@Z!e9-h@Y-g&jH$H=pZU!Lp]H!['E#*YIi=dpi=Gi`;Xh_-r2H]uC2%C55O_07_&l + nOVnTDWrkKV0"b1@*T*f'sbI7S'i/a%Q5;qI2rH7$/r)$eia85``1R,ip_=XXDli&u+nAi8+_&m=]r]:ek-BKAar?b-SXRFg9O1ZR)((>\a[.nA.L]/FtP$OX'W + M$+2ls\QEd.p>[0&a/gEnTc*?LP#]h-]!1dfB+CF;Bu[SXhet'7NZLp=Iik/)>DO\TIf?VB + 26T,Z?;>VQVsLGMEaq,3':RVG + rN/_BG^l<\+8Jji=Nde[WEu@MSBF$=+o;>=3),o1[):W'hO]";-7=-3*kGip&(_l9<5"[8T + -(ijHLTnj6iCK701L5J)X0C_El8bWHo]2-]#;-=a#Qe1A+W7*Rsmd`7JXNB:7>olV6Q3IBO + QreJ$^QiP7Adt5L&l3g>'c.aJc0g:9h@7oKVcGYM]3TU#0p;n*k[G.tg.\k0MEA6V$NSq*R + dto#DonJIHOUFctP"R#Sg=Hja]sBaM_7DX3Rq[d?kITqo5q)?.Dnp]H"pG^oAYqDJfbJ)Ck + 8JNjq@Vp&9:kaT$[^$l:+^`W>]+b/bqle4N.r..*%#Q@iB+8RX?l9X>=r..*%`tJA.[QOL% + 8+6c_^`W=r]0G#\-beaRJ)Ck8JNoIlVpnO8YeV@U"+T7T&+@hCJ*[Sni",a:5F1f:4[4+E5 + O\pW^`V8.0n7pQd1\b3%"I1Gr..(uo>fKkm*J&V&,^uCn4+7D.V^IpH4]iRJ)Ck8Oi8CK^L + @KJ^`W=RJ,'#MrB4*S"+T5>r3;>J&tZbo&,^uCn;$I.M8#dbV$[.:+8>qfIX$?5!6GIs5O\ + pW^`Nm3MGJA#2uVh>!!!*\l5ilkl?LOrM)UoOS!DF:rK*]QmH2-HQj,!Oi.*qs + P(^U.F!c.$:(W3*#"X5p'6Bl=dD4IL4F-oHfZBNjU9O^J%VfH:-6i\/O>S>j()gWgb62[ + Q6$h30S35&B%6+EeUKaXf8i^W/A5!EkgH+]3Sdd`2fe*&iSMKLHh?dihN&Nd6ruOdSuSC[T + 8ZVLg^!aflVCjZI$)kc*\U1sR/Hg;f3d;0RcL@7R8,c\3k/O:-U8rT8l0B>[QV;J4gQUoUG + >2W%kAaal11:3Rtr2!D/>f?N02;UlFL^SLm,P=J$7oSX^7Cl37F)LWq39)p%"/Bs569PRJl + Yfq.&7n=Gd%(&kF'n4C=K:5O#R=s3D6O)ko9iYGfPSagib.DS*>/XWf?lb%umpdOWd(=q1C + DcbLXt"+T6iW.@2&#QDL\;$2=+&,^uCU'CV4+8>qf6k0'F5O\pWL`6$jJ)Ck8&N0%_rkKWN + ,AQ-Ir..*%8(>r + "1gU#QDL\p`jP3&,^uCn42!D+8>qfi/agf5O\pW^`QDUJ)Ck8JNok5rkKWN"+N^u/H4-Xe] + [&M86X*foWt(5AXltMIc[2_s7u+2s3?H,)Ws%sR+F"n7!`1Wkm4D;0XIeXG6kJQlM`Gc]7C + -`_&e4VqG/A"VX9ekBklSU%5AgW'&6aP>pZs+I`pKIrs54GI;,M2c^bMI_=?a!6T9d2h + H1EnhbaL\VDi]>NJP;AhMRM@YpEkpb`5,2KYH!,/tI)jSG#'g[skV8/4*FXK=JDP1Wq=San + e(:]T1HGfhn^=k4Qjq_,jHmK-q<%Z8hq7s`jF4H(2j@7BP@pVfq\==sdIPEF\ek(tZ+2$3> + 1f<$dR!.:la1ent + k;b)ZdO4g&]3OHl_*4OTIeqK;dN7628UQLt,i>LUuke_8o1S%IY/tWpNF?a + >+FGRXZm;(cc7l<.@l4(`;,H'7Mg0AK48m+c$Gq--6.49qEKD,jrg372eRYtr^?!+Ka8p[N + kXT%eGQZGOVc4/nN13Z%JJgFhuQn8YN[/SL"cRYUT$1=>=in]48K&\\-0tB$/U3!&cN`&5Y + M7uB(L6=<%=]7slo:opoP7uM!5OcbR&)DR(-9VsZa]NK*Y0'Kl9/c75bNi9YZ2Jn@'1%dCE + ::U!4*q9qPu0%l&RVo$RN45S(*:]^LWj*F[[>abd"Ph4r6"&WLH7`G8JSEiPBqr`\Wh/l)6 + I5dOCEVm-PDB(/kgXr"VZ;Qg=O+^p]H$(O/RUon-As.+&N,ii",a:5GAA]^`W=RImabDJNr + W/reMKf"+T5>r"1gU#QDL\p`jP3&,^uCn42!D+8>qfi/agf5O\pW^`QDUJ)Ck8JNok5rkKW + N"+N]Jr..*%#QBMtp]H$(&,["sn-As.+8@'qi",a:5O_1m^`W=RJ)HBdJNrW/rkKUQ"+T5> + r..),#6)C[p]H"6NOASG<15(b5KUt>h:]:;c@3kI6$mM<+[&joX]TRLH:6-NaQ9a[Ae@>1J + nNC;RC=Sd&[MWa$eATgkC1C_JH[FQbH>L+\GfaW^.=[j0sLR)W[WNa2EGmS_o.>1D5b^Yc6 + 5=lPWdrh>+U$qjlD!)#<84A*4g?M'O#]ph3n^e33Bm(Q3a4]bC\(EbSHgVr)rf-ba+i+\hS + Nk[]A.FYBY).:N2Y3ruERbRqLT-a&!tjf(BOq+8B?X_fq&oDIKj;,$.cG?LF.sn8^&fuH + 5BI[KhLh3C=^=s5;e':51[0c+`C9VI:3;.UI3Ca_jQZ6FC%LPgB9oaG0@?+celFPG-Jj^k7 + _2aP2ficj6C0p2:L9")&)G<;=[hZBoqVgK]"\n!r!I0GEj`IUb_U!)psIOt/a\pj)9N51d_ + jRF@R`9spk,i+JNrW/ra8,'r..*%#:"!A&,^uC(f:Bbi",b%L`6$jJ)Ci,eA*1$"+N#!#6) + C[pk'/9n-As.+,m.L5O\pW?pkJ%JNrW/,AQ-Ir..'L;$2=+&,a8")YaDai"&<,^`W=RIk[T + $rkKWNK+U!1#QDL\N2V:ln-Ar#6k0'F5O\n&C>]X#JNjuK"+T5>r5"1-Abr12,(DH")B&M4 + iracbehbn;:JJNJruL\P3X0IiO@H/,Q`&$MfB"e5JkGY]k0;:u&OFE+,M#(;2ua=Q<)sJ~> +Q +Q Q +showpage +%%Trailer +end +%%EOF diff --git a/testfiles/cli_tests/testcases/export-dpi_expected.pdf b/testfiles/cli_tests/testcases/export-dpi_expected.pdf new file mode 100644 index 0000000..82c6045 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-dpi_expected.pdf differ diff --git a/testfiles/cli_tests/testcases/export-dpi_expected.png b/testfiles/cli_tests/testcases/export-dpi_expected.png new file mode 100644 index 0000000..cf1781e Binary files /dev/null and b/testfiles/cli_tests/testcases/export-dpi_expected.png differ diff --git a/testfiles/cli_tests/testcases/export-dpi_expected.ps b/testfiles/cli_tests/testcases/export-dpi_expected.ps new file mode 100644 index 0000000..a92caa7 --- /dev/null +++ b/testfiles/cli_tests/testcases/export-dpi_expected.ps @@ -0,0 +1,319 @@ +%!PS-Adobe-3.0 +%%Creator: cairo 1.15.10 (http://cairographics.org) +%%CreationDate: Thu Mar 5 09:46:34 2020 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 3 +%%DocumentMedia: 61x32mm 173 90 0 () () +%%BoundingBox: 0 0 173 90 +%%EndComments +%%BeginProlog +/languagelevel where +{ pop languagelevel } { 1 } ifelse +3 lt { /Helvetica findfont 12 scalefont setfont 50 500 moveto + (This print job requires a PostScript Language Level 3 printer.) show + showpage quit } if +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +/cairo_set_page_size { + % Change paper size, but only if different from previous paper size otherwise + % duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size + % so we use the same when checking if the size changes. + /setpagedevice where { + pop currentpagedevice + /PageSize known { + 2 copy + currentpagedevice /PageSize get aload pop + exch 4 1 roll + sub abs 5 gt + 3 1 roll + sub abs 5 gt + or + } { + true + } ifelse + { + 2 array astore + 2 dict begin + /PageSize exch def + /ImagingBBox null def + currentdict end + setpagedevice + } { + pop pop + } ifelse + } { + pop + } ifelse +} def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageMedia: 61x32mm +%%PageBoundingBox: 0 0 173 90 +173 90 cairo_set_page_size +%%EndPageSetup +q 0 0 173 90 rectclip +1 0 0 -1 0 90 cm q +0.9 0.950196 0.9 rg +90 45 m 90 69.852 69.852 90 45 90 c 20.148 90 0 69.852 0 45 c 0 20.148 +20.148 0 45 0 c 69.852 0 90 20.148 90 45 c h +90 45 m f +0 0.501961 0 rg +82.5 45 m 82.5 65.711 65.711 82.5 45 82.5 c 24.289 82.5 7.5 65.711 7.5 +45 c 7.5 24.289 24.289 7.5 45 7.5 c 65.711 7.5 82.5 24.289 82.5 45 c h +82.5 45 m f +Q q +81 0 92 90 re W n +q +81 0 92 90 re W n +% Fallback Image: x=81 y=0 w=92 h=90 res=300ppi size=432000 +[ 92.16 0 0 -90 81 90 ] concat +/cairo_ascii85_file currentfile /ASCII85Decode filter def +/DeviceRGB setcolorspace +<< + /ImageType 1 + /Width 384 + /Height 375 + /Interpolate false + /BitsPerComponent 8 + /Decode [ 0 1 0 1 0 1 ] + /DataSource cairo_ascii85_file /FlateDecode filter + /ImageMatrix [ 384 0 0 -375 0 375 ] +>> +cairo_image + Gb"0WGB=Pn\c6Z%%44*ud^u(.KMYC`,R3VR5&gnFp9BgFm_na4)sko\Vu^)(,`76nLgk[36 + P9Z$5sk2j#t_(uHIo;7Valj8d58Y[Ur..*%#:"!A&,^uC(f:Bbi",b%L`6$jJ)Ci,eA*1$"+N#!#6)C[pk'/9 + n-As.+,m.L5O\pW?pkJ%JNrW/,AQ-Ir..'L;$2=+&,a8")YaDai"&<,^`W=RIk[T$rkKWNK + +U!1#QDL\N2V:ln-Ar#6k0'F5O\n&C>]X#JNjuK"+T5>r5"1-p]H$(%n>'U4DH$&RNFCCUJ + %AZ^8AogM?k[i'UuSi@73U.K$8?#o!iE*$?QHqo!S9>L6B]jb)4m[rHF^qM'-E9hue + JqHlgBaNQmIF;jpcTF$ir5(&.P[(I))PMnUD&CZP%VDpgH?1"I;M.^SQ0ga@]@*DJT+pW + d(H6ur1nmLQrN89Fe,r4i8[:3q#8AWb/"$2S15cn&56Zf["d>o:/7g&-s-KIZ5[8l0N]'#3 + OMl=n],;U-T1?-L#Rj]:QQ)Bu^Y>p9$md*r&FCi6>sTBrTEUs^Mkr7Q;GCl%/d^A%;l4*t# + "?V,ce=rF_gH/ruIA9RHYB!d@t'XihNK$X\e0!01jFEb@V`lU*Soh='WVE_1s[$otmQ9VM; + +8B?("+T5>r..),#6)C[p]H"6%K(cAn-ArK)YaDai",_t2=LkM^`W=rC>]X#JNrWoeA*1$" + +T6iW.@2&#QDL\;$2=+&,^uCU'CV4+8>qf6k0'F5O\pWL`6$jJ)Ck8&N0%_rkKWN,AQ-Ir. + .*%8(>r"1flpd> + )Ic%%Vf3LTX!WA8891c*#qB*OhhfX8cI:pDpYVr!QCbh#e54l!#M5S[q")B4l-](# + Kc(o,Y-G.@[TZn5j]#`i8M*TK;/kPrd4JCQ[g4(j)h%gs!i1#r^ur9>/R:Q$pmfg=aCS#i0 + )>:d`ugr9Flq^[Jj,SA4EkZKX4]eF=%'1CFEs'kS22P5=N;Z.7j9`6__]/tAQ38TW\q?;\X + Wp[;klp=bq#8%6sl1e1%MDMRN&X,>$Zc@PG-BC^[utWH<4*S=O*;/(4$2H*d_65K>4%5?:qb + +n2PuX,.roqF:G>P.SPE_*ej4aGe1Tk?%TGpn.P/l>P*4Bl:$@fO8+(L3jGtT%@pBK)(!uo + jQEUe[t^EmehC@p?:bpPFJRbJ+rpqGePla@kafH*ePg7:7/J:g=4qW3PE#ieb,9[X5[,`/o + Qm-k0'5W'0T:;;[F"1F9Gc>_4UF[>jJ"sP77HG/%la^(39--=\e-8c`K=Eo$8]_9M6L?j*\ + n0+X=WiE2"TL;tlJea[(%<,r..),#6)C[p]H"6%K(cA + n-ArK)YaDai",_t2=LkM^`W=rC>]X#JNrWoeA*1$"+T6iW.@2&#QDL\;$2=+&,^uCU'CV4+ + 8>qf6k0'F5O\pWL`6$jJ)Ck8&N0%_rkKWN,AQ-Ir..*%8(>>=%07fbK7Nsj7GR3GKS]c^[]4T=T4973Kf + Dq4Fa:1mK7"iB9N:jf)fe10)h(<159 + sO^L,+DOsBZE@1>e-X&efO'M[>@t1-t&@4)T@tsCg3m;@V-TZj)"!'=YBKl%dk:fP\ba0XQT,eZ==;3N`]B8LF;$M"^0N)!q^(tm.l0a-r + ]Z';]8s;'dE"OCbG1BW``d!H.VWuCHKr&&I=Y=e-B?B&h];XN#=gu9o\n1.l)LX#FC0/tAm + Lt>53qk-Z]BPAjIU-C7gNaZ7@#qVU + oue/.8tjDi>mk#U&6df!I;709*>mn^4&\?VlmGn>9!Bs+?M.O$\.I+ER@5/X[rnMi",a:5=5;4cmKIb&,^uCn4-f7b)Bm7 + $iGQ?n-AsjnGZ6Aoel4DrkKWN,K(\(4Wao\5O\pW^`Q5Ee"/4(^`W=RJ)HA6r1QXcp]H$(& + ,[%!:RRoXm=-eeJNrW/r`I76Vg^96rkKWN"+Mlb:Plf]NVDGk&,^uCnAfSK'Y!m">2^"iJN + rYEUAhOi?G)6ommLbW7V,)0MUfX+`&PDXi9cqfB6&=unG>Rs%#$q>k0G](r*kmLf:N@u'Bg + ]`NcjLN,IZRZ#2^B6o'F+iBE"_C[dHq6?1jEZ0f0`+NCH[?-:2FS`tNo]U\6(Q3m+;bO#R%4Q%2r5"AG5h96q9M-[p=46b4M7:-HkdGf + oPl"kVr^2:NS42)]OM$=s!3Brq6IrmWgTOg.:cVgMd!E*Db4TNUkRk&X^HG0.,,Bg&osi,= + lUkL)/H1>JOV?W3p%d5m-jiq/c4F*-T>8$GSaP:$-#PT29LhE1YlRFF#?+.VD62#6@cJua! + U1@`b]oYUP]07fSW+6-.n%/ph2jNiG)"e8cpTr/qY55j"0Sp,JZ)*&`LKO7ZVrQ3uYdrRb- + oHRPQO?]8Tq2Th%aSS8;61F(7<>[7E&,]:>nAi]MU$,VlrkKUE#(N`3TC4Fai",`_;Xha'r + V?F*#l_U],mAd7_Op;R#_PA27nLN=S+)2JBPN&^n4-p'+'\95]!`OV^`S76Ir*)rU7.gRjs + Kj*J!:ZFYn_be)H.+CJNla6rn*;s`APX,#R5e:O(*]*2WPB[CbW^JpnO57O)=\kAgi9rn4, + 4L+5?@aW2<*eVh75'7iB,bS+EOG?NP`8O"u;O2WmkRFmd/)L[YQJ-m8:pF8k6"jWp*:_&rH + IJDcJ?qK'@.3rFOn$4-X`U-FK[M7^_cW'=ATY&\t!4o@Us + &H&e1J#JU;'UCst+gZf50+;(+Bkd@jl$K;Ni/\iTZ,^DHa)BtuJB;&G%\*DWQ$S>:Z/fDPf + mi2Tr`&&+BG.K/?+F*[1SZ]m=rOMt?L!$3ro`(p\XXFi/t[>/OY5,pf:qR5h,"+TYg=o + @OHdVZYcGZk/Cn=(Q20g9)Z*hGq2Qt%Sc3$#S[kXti>]#Hd7^i6;.eMK*?aQnmVVod?KE2" + R&QR*_F3c[(A.!706h"f1c&rOqnb9ZA/6=Qg[^JBbD:V"Ap?, + +[hEFU3!%fa))m^20,553)2lg5kfhO!mke?)>leKU=6Xt)fB%(J-PT_c=h-aE;bfsV-RLcabN1=N]LUAdPnX']_P>S@M%A-8eW]p)"jCW&&r'>KCr36HU3qP$er'?2Hr`Fe;a-)M"+(U/C + +)CA$MTCU(5KM]55OJ(j[63!6rj;\q5k#"--MZ%bJ+haTCJfRV5<'+%OM(gIg?HquK)T=YI + uYluLHil:Jj8Ypk9jJ`@SqX/s!])cK+o!sBr=2A3'J"H)f8H%_U??[&$i7o:](*1N-/\-P? + aE$39fprr+Y=1_.%HC%oPr=q@g^nqelESqGVDt>^HnR)ZG$#k=a,RjYT24I_a3Y&q*?C2NO%Jp>;R + qWh6k0C3ci?d`^)Qo+O('D-"WV*h-AcS)6rTV\XM=E,!8=6(W^`lkmV(V&_5^;"Yr4Z#;'( + 'qIJ54$VWIV&QiGMDS)-'rb2UF2"p + 4nqdo^qdO]WZ\Zh!f$i?+4u/5.c'4kJ3^-s!aihgP9lpm1+W-^fp\F7fN%$lhA\UGMYOQ2Q + ,1$^:kb:"$NYQg5CYcf2iIO=p$,TQKBsu\dij31G`T)-59>Y&n-@WBn>AYQFEZqs^$rVpr,GAKK0RG + +Jj1:HLY?rrTB6!EG$a&4Iup3(Ilcl@g.Q35lAsOS0DL1d,l#hcJ,X]jNu%D$qpk*'#MD]< + Vka6Mo,hUBIlnB-i=E5Xi(llAl/pDGV(BT+r]Kj*0_g87'E%*n5?)0*UuhMO-`Gu7ZBO4-" + Fmjeh/56&<:MI5KBslY,Jg"KiCbcccVr_QLHe5>B8>9rj;V9+S^R''E"!N%i(ZJDE3 + 4#rWj)56&H!J8FpKGm9Q%LVsTNF_&oJ?i/]^E2F_VlS3Llj((U+l'GLD,k0MhE\X)ibm>iC + ,+/G:^O#2@cE;U64o>``F(&f>&#l[BdkKWsD>0`bK56=RY/.#YbIuj_`5LTIln.EV-p>=OC + \,!"K?1T7l*h.HTST&lJhgn!_CbkkYc@L2rqYDd=ea=R+=P`]5M9\h + l)e_RELRc$-@:R+GD*$qT"'_Tb?o//Efklr'>Kfsu$iEa<#ptdejE]'``aA%q5`mV"q@g`_O_QitUYZIulerJ#)m-%_s1]K:'IsC&Y%EeaNhC+ + D1+5RQmC#T(!!a+D1+5RQpTC&,\`ki/]^Er,;C8d%=G8KBsfW,L)joGPC+qGPHfX_&mCj*5 + $dJErP"gccAk55LTHirnod6JNnr.r/p+P^Ae[m&3oq7c(FLLE6J'0huA>q,krV/TD6'Dp]H + !W1\`32T?b`Qn!2b9TKi5MccA;%58*nYWTr.[HogO'p:ik9P$J"=ec$@P[L\MY&,-#ikWb5 + MrM/HP^]*s\L'Yl?f_6-Nm3a0,,cs6/**QLi-ShXsJ8[/.-( + X:4D:pk+5)J#)lr-Y/8!r)(G6i"*,Ki/]^EejlpQS3LltDX[na6h!U75CPQi/E`'T5n/=*4 + Tp81q%K2$3#T@%'uHgDXhRlp^mEI,mAcZl;(LEY">H]QLi-Ad0'@j3skn.e=Y, + PUF"jIIXg)Nn.5F_8dbQ>e=Ve^GPIZr*V-,q&-'EeUnlpE8,ZR*[E>6CG[9om5KHTO599DE + n4oWFrFt:NO22p6Yi&<4r1TH-YoqG7rj;Wn2rG3E=R`4UrX@U;<#RsfoV_(p^;D%gYi&<4r + ;"fu5?ZL.X]N!K+(RUPT+:\'-%:b@rRlXB48_D<'E%*V5NafJ1&]c>[n6-L:KoXD^E\Lu6a + dNimb/]\+G%YucT;Bg@JXKaqsQL8r3\chj%oK[l^t]3Wd*'(F%^KD]BT(>!cJ;rNEA6_EtA + Ah$0rZY]N0Nn\THk"rq':dT5eNK/V,mpZ2Y8g(l)@5DL,kTVppT'9<3TUgjuS&Pqdlc6YenPkV-H^>h4JoURtgI:-^.cMS,"4#qg6,UGMMHE2AdEU,uA + 9!3qbNtL4nQChC\=i=sC(5=ErI=$1;Tep,CcRDYVVm"^qma"t^?SVLN/k\BuFSJDTOiJ6%) + FVnq:G!nP`q?Y'B^a=^Hm`m%j>Ue7q]PZd"@f:2iPVapWoqHo,JQA8jg)s=Sm5`3l-E^4rmdbB*)AcT*dllV;H!"^Sc8EAoXCb`V]VF`5KM]55OJ(jW?fT[re.)&^`R* + *_4PoIPH2s9W'B5O_r2-3\)<<)piA:VBKPa85=eA^+)CB/e:U]h#MEPT`u=kirssV;%oQ

@2a:;W.re.,'_&m3[_&or]*)/I4RF/1@I + DI#$]\niQIld5I48_AO$iYtTVaKpc_]P\Yi/^ui3qU]Zr':YtrWr/Z7.Q1piWF[lP(Gn"r< + Z4,L-NbNK0KG9OmqiDIuohF5GJ(mD$Qiq-s'VgIn#-%?9pKq.^VnumNM + FbS%sC5R&3EtU;9=7WNL.(k[>R3=/A`(&GP"c*iC#%Y=6Ha43 + +*h.qhitVjRK,t-S[9:8hNi`WlgJ?ko-!33[]0tU9fb;OnR8u9^h6"PBloekLT*IQfjul5B`>Drqp + HYO!a]tS/a5.P5cfLNUn1b_3#6pdR2+_jAO+NuJ#MA$Ypj5EURoX$?%pI\/5"=or\)SoXa4_6pk/3[R%t"L;WhmM+Q@ + N">hP8;1I"DdbkZ6Z1[%9/c'N?ru)+apk,Xnpg]6]gb62KJ)H@hJj6^-:O7:Ir..'># + l`JH5K,M!R?a30#Q?+opg\CEBPY$F#Q?+rpg[P-PT!0YY6KL7O"u;O[cJ%uLVY)-UR9u#%J + `qu.r'@Z!e9-h@Y-g&jH$H=pZU!Lp]H!['E#*YIi=dpi=Gi`;Xh_-r2H]uC2%C55O_07_&l + nOVnTDWrkKV0"b1@*T*f'sbI7S'i/a%Q5;qI2rH7$/r)$eia85``1R,ip_=XXDli&u+nAi8+_&m=]r]:ek-BKAar?b-SXRFg9O1ZR)((>\a[.nA.L]/FtP$OX'W + M$+2ls\QEd.p>[0&a/gEnTc*?LP#]h-]!1dfB+CF;Bu[SXhet'7NZLp=Iik/)>DO\TIf?VB + 26T,Z?;>VQVsLGMEaq,3':RVG + rN/_BG^l<\+8Jji=Nde[WEu@MSBF$=+o;>=3),o1[):W'hO]";-7=-3*kGip&(_l9<5"[8T + -(ijHLTnj6iCK701L5J)X0C_El8bWHo]2-]#;-=a#Qe1A+W7*Rsmd`7JXNB:7>olV6Q3IBO + QreJ$^QiP7Adt5L&l3g>'c.aJc0g:9h@7oKVcGYM]3TU#0p;n*k[G.tg.\k0MEA6V$NSq*R + dto#DonJIHOUFctP"R#Sg=Hja]sBaM_7DX3Rq[d?kITqo5q)?.Dnp]H"pG^oAYqDJfbJ)Ck + 8JNjq@Vp&9:kaT$[^$l:+^`W>]+b/bqle4N.r..*%#Q@iB+8RX?l9X>=r..*%`tJA.[QOL% + 8+6c_^`W=r]0G#\-beaRJ)Ck8JNoIlVpnO8YeV@U"+T7T&+@hCJ*[Sni",a:5F1f:4[4+E5 + O\pW^`V8.0n7pQd1\b3%"I1Gr..(uo>fKkm*J&V&,^uCn4+7D.V^IpH4]iRJ)Ck8Oi8CK^L + @KJ^`W=RJ,'#MrB4*S"+T5>r3;>J&tZbo&,^uCn;$I.M8#dbV$[.:+8>qfIX$?5!6GIs5O\ + pW^`Nm3MGJA#2uVh>!!!*\l5ilkl?LOrM)UoOS!DF:rK*]QmH2-HQj,!Oi.*qs + P(^U.F!c.$:(W3*#"X5p'6Bl=dD4IL4F-oHfZBNjU9O^J%VfH:-6i\/O>S>j()gWgb62[ + Q6$h30S35&B%6+EeUKaXf8i^W/A5!EkgH+]3Sdd`2fe*&iSMKLHh?dihN&Nd6ruOdSuSC[T + 8ZVLg^!aflVCjZI$)kc*\U1sR/Hg;f3d;0RcL@7R8,c\3k/O:-U8rT8l0B>[QV;J4gQUoUG + >2W%kAaal11:3Rtr2!D/>f?N02;UlFL^SLm,P=J$7oSX^7Cl37F)LWq39)p%"/Bs569PRJl + Yfq.&7n=Gd%(&kF'n4C=K:5O#R=s3D6O)ko9iYGfPSagib.DS*>/XWf?lb%umpdOWd(=q1C + DcbLXt"+T6iW.@2&#QDL\;$2=+&,^uCU'CV4+8>qf6k0'F5O\pWL`6$jJ)Ck8&N0%_rkKWN + ,AQ-Ir..*%8(>r + "1gU#QDL\p`jP3&,^uCn42!D+8>qfi/agf5O\pW^`QDUJ)Ck8JNok5rkKWN"+N^u/H4-Xe] + [&M86X*foWt(5AXltMIc[2_s7u+2s3?H,)Ws%sR+F"n7!`1Wkm4D;0XIeXG6kJQlM`Gc]7C + -`_&e4VqG/A"VX9ekBklSU%5AgW'&6aP>pZs+I`pKIrs54GI;,M2c^bMI_=?a!6T9d2h + H1EnhbaL\VDi]>NJP;AhMRM@YpEkpb`5,2KYH!,/tI)jSG#'g[skV8/4*FXK=JDP1Wq=San + e(:]T1HGfhn^=k4Qjq_,jHmK-q<%Z8hq7s`jF4H(2j@7BP@pVfq\==sdIPEF\ek(tZ+2$3> + 1f<$dR!.:la1ent + k;b)ZdO4g&]3OHl_*4OTIeqK;dN7628UQLt,i>LUuke_8o1S%IY/tWpNF?a + >+FGRXZm;(cc7l<.@l4(`;,H'7Mg0AK48m+c$Gq--6.49qEKD,jrg372eRYtr^?!+Ka8p[N + kXT%eGQZGOVc4/nN13Z%JJgFhuQn8YN[/SL"cRYUT$1=>=in]48K&\\-0tB$/U3!&cN`&5Y + M7uB(L6=<%=]7slo:opoP7uM!5OcbR&)DR(-9VsZa]NK*Y0'Kl9/c75bNi9YZ2Jn@'1%dCE + ::U!4*q9qPu0%l&RVo$RN45S(*:]^LWj*F[[>abd"Ph4r6"&WLH7`G8JSEiPBqr`\Wh/l)6 + I5dOCEVm-PDB(/kgXr"VZ;Qg=O+^p]H$(O/RUon-As.+&N,ii",a:5GAA]^`W=RImabDJNr + W/reMKf"+T5>r"1gU#QDL\p`jP3&,^uCn42!D+8>qfi/agf5O\pW^`QDUJ)Ck8JNok5rkKW + N"+N]Jr..*%#QBMtp]H$(&,["sn-As.+8@'qi",a:5O_1m^`W=RJ)HBdJNrW/rkKUQ"+T5> + r..),#6)C[p]H"6NOASG<15(b5KUt>h:]:;c@3kI6$mM<+[&joX]TRLH:6-NaQ9a[Ae@>1J + nNC;RC=Sd&[MWa$eATgkC1C_JH[FQbH>L+\GfaW^.=[j0sLR)W[WNa2EGmS_o.>1D5b^Yc6 + 5=lPWdrh>+U$qjlD!)#<84A*4g?M'O#]ph3n^e33Bm(Q3a4]bC\(EbSHgVr)rf-ba+i+\hS + Nk[]A.FYBY).:N2Y3ruERbRqLT-a&!tjf(BOq+8B?X_fq&oDIKj;,$.cG?LF.sn8^&fuH + 5BI[KhLh3C=^=s5;e':51[0c+`C9VI:3;.UI3Ca_jQZ6FC%LPgB9oaG0@?+celFPG-Jj^k7 + _2aP2ficj6C0p2:L9")&)G<;=[hZBoqVgK]"\n!r!I0GEj`IUb_U!)psIOt/a\pj)9N51d_ + jRF@R`9spk,i+JNrW/ra8,'r..*%#:"!A&,^uC(f:Bbi",b%L`6$jJ)Ci,eA*1$"+N#!#6) + C[pk'/9n-As.+,m.L5O\pW?pkJ%JNrW/,AQ-Ir..'L;$2=+&,a8")YaDai"&<,^`W=RIk[T + $rkKWNK+U!1#QDL\N2V:ln-Ar#6k0'F5O\n&C>]X#JNjuK"+T5>r5"1-Abr12,(DH")B&M4 + iracbehbn;:JJNJruL\P3X0IiO@H/,Q`&$MfB"e5JkGY]k0;:u&OFE+,M#(;2ua=Q<)sJ~> +Q +Q Q +showpage +%%Trailer +%%EOF diff --git a/testfiles/cli_tests/testcases/export-filtered-clones-mp.svg b/testfiles/cli_tests/testcases/export-filtered-clones-mp.svg new file mode 100644 index 0000000..59e6bb8 --- /dev/null +++ b/testfiles/cli_tests/testcases/export-filtered-clones-mp.svg @@ -0,0 +1,285 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + plain item + + + filtered item + + + + clone of plain item + + + + clone of filtered item + + + + filtered clone of plain item + + + + + + + + + + + + + + + + diff --git a/testfiles/cli_tests/testcases/export-filtered-clones-mp_expected.png b/testfiles/cli_tests/testcases/export-filtered-clones-mp_expected.png new file mode 100644 index 0000000..6bad12e Binary files /dev/null and b/testfiles/cli_tests/testcases/export-filtered-clones-mp_expected.png differ diff --git a/testfiles/cli_tests/testcases/export-grouped-mp.svg b/testfiles/cli_tests/testcases/export-grouped-mp.svg new file mode 100644 index 0000000..59fad92 --- /dev/null +++ b/testfiles/cli_tests/testcases/export-grouped-mp.svg @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/testfiles/cli_tests/testcases/export-grouped-mp_expected.png b/testfiles/cli_tests/testcases/export-grouped-mp_expected.png new file mode 100644 index 0000000..f4b830d Binary files /dev/null and b/testfiles/cli_tests/testcases/export-grouped-mp_expected.png differ diff --git a/testfiles/cli_tests/testcases/export-height_expected.png b/testfiles/cli_tests/testcases/export-height_expected.png new file mode 100644 index 0000000..e6ab7ed Binary files /dev/null and b/testfiles/cli_tests/testcases/export-height_expected.png differ diff --git a/testfiles/cli_tests/testcases/export-id_expected.emf b/testfiles/cli_tests/testcases/export-id_expected.emf new file mode 100644 index 0000000..3c8ff73 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-id_expected.emf differ diff --git a/testfiles/cli_tests/testcases/export-id_expected.eps b/testfiles/cli_tests/testcases/export-id_expected.eps new file mode 100644 index 0000000..0dd5840 --- /dev/null +++ b/testfiles/cli_tests/testcases/export-id_expected.eps @@ -0,0 +1,82 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: cairo 1.16.0 (https://cairographics.org) +%%CreationDate: Tue Mar 31 01:06:36 2020 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%BoundingBox: 0 0 75 75 +%%EndComments +%%BeginProlog +50 dict begin +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageBoundingBox: 0 0 75 75 +%%EndPageSetup +q 0 0 75 75 rectclip +1 0 0 -1 0 75 cm q +1 0 0 rg +0 0 75 75 rectfill +1 g +18.75 18.75 37.5 37.5 re f +Q Q +showpage +%%Trailer +end +%%EOF diff --git a/testfiles/cli_tests/testcases/export-id_expected.pdf b/testfiles/cli_tests/testcases/export-id_expected.pdf new file mode 100644 index 0000000..ba9a322 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-id_expected.pdf differ diff --git a/testfiles/cli_tests/testcases/export-id_expected.png b/testfiles/cli_tests/testcases/export-id_expected.png new file mode 100644 index 0000000..5ebc531 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-id_expected.png differ diff --git a/testfiles/cli_tests/testcases/export-id_expected.ps b/testfiles/cli_tests/testcases/export-id_expected.ps new file mode 100644 index 0000000..c7cb7a2 --- /dev/null +++ b/testfiles/cli_tests/testcases/export-id_expected.ps @@ -0,0 +1,119 @@ +%!PS-Adobe-3.0 +%%Creator: cairo 1.16.0 (https://cairographics.org) +%%CreationDate: Tue Mar 31 01:07:13 2020 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%DocumentMedia: 26x26mm 75 75 0 () () +%%BoundingBox: 0 0 75 75 +%%EndComments +%%BeginProlog +/languagelevel where +{ pop languagelevel } { 1 } ifelse +2 lt { /Helvetica findfont 12 scalefont setfont 50 500 moveto + (This print job requires a PostScript Language Level 2 printer.) show + showpage quit } if +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +/cairo_set_page_size { + % Change paper size, but only if different from previous paper size otherwise + % duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size + % so we use the same when checking if the size changes. + /setpagedevice where { + pop currentpagedevice + /PageSize known { + 2 copy + currentpagedevice /PageSize get aload pop + exch 4 1 roll + sub abs 5 gt + 3 1 roll + sub abs 5 gt + or + } { + true + } ifelse + { + 2 array astore + 2 dict begin + /PageSize exch def + /ImagingBBox null def + currentdict end + setpagedevice + } { + pop pop + } ifelse + } { + pop + } ifelse +} def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageMedia: 26x26mm +%%PageBoundingBox: 0 0 75 75 +75 75 cairo_set_page_size +%%EndPageSetup +q 0 0 75 75 rectclip +1 0 0 -1 0 75 cm q +1 0 0 rg +0 0 75 75 rectfill +1 g +18.75 18.75 37.5 37.5 re f +Q Q +showpage +%%Trailer +%%EOF diff --git a/testfiles/cli_tests/testcases/export-id_expected.svg b/testfiles/cli_tests/testcases/export-id_expected.svg new file mode 100644 index 0000000..6dfe9e7 --- /dev/null +++ b/testfiles/cli_tests/testcases/export-id_expected.svg @@ -0,0 +1,110 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/testfiles/cli_tests/testcases/export-id_expected.wmf b/testfiles/cli_tests/testcases/export-id_expected.wmf new file mode 100644 index 0000000..fba7d63 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-id_expected.wmf differ diff --git a/testfiles/cli_tests/testcases/export-id_export-id-only_expected.emf b/testfiles/cli_tests/testcases/export-id_export-id-only_expected.emf new file mode 100644 index 0000000..d34b0d4 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-id_export-id-only_expected.emf differ diff --git a/testfiles/cli_tests/testcases/export-id_export-id-only_expected.eps b/testfiles/cli_tests/testcases/export-id_export-id-only_expected.eps new file mode 100644 index 0000000..1f84f07 --- /dev/null +++ b/testfiles/cli_tests/testcases/export-id_export-id-only_expected.eps @@ -0,0 +1,80 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: cairo 1.15.10 (http://cairographics.org) +%%CreationDate: Wed Apr 1 00:39:29 2020 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%BoundingBox: 0 0 75 75 +%%EndComments +%%BeginProlog +50 dict begin +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageBoundingBox: 0 0 75 75 +%%EndPageSetup +q 0 0 75 75 rectclip +1 0 0 -1 0 75 cm q +0 0 1 rg +0 0 75 75 rectfill +Q Q +showpage +%%Trailer +end +%%EOF diff --git a/testfiles/cli_tests/testcases/export-id_export-id-only_expected.pdf b/testfiles/cli_tests/testcases/export-id_export-id-only_expected.pdf new file mode 100644 index 0000000..2febd2a Binary files /dev/null and b/testfiles/cli_tests/testcases/export-id_export-id-only_expected.pdf differ diff --git a/testfiles/cli_tests/testcases/export-id_export-id-only_expected.png b/testfiles/cli_tests/testcases/export-id_export-id-only_expected.png new file mode 100644 index 0000000..3e0ef96 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-id_export-id-only_expected.png differ diff --git a/testfiles/cli_tests/testcases/export-id_export-id-only_expected.ps b/testfiles/cli_tests/testcases/export-id_export-id-only_expected.ps new file mode 100644 index 0000000..0947232 --- /dev/null +++ b/testfiles/cli_tests/testcases/export-id_export-id-only_expected.ps @@ -0,0 +1,117 @@ +%!PS-Adobe-3.0 +%%Creator: cairo 1.15.10 (http://cairographics.org) +%%CreationDate: Wed Apr 1 00:39:28 2020 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%DocumentMedia: 26x26mm 75 75 0 () () +%%BoundingBox: 0 0 75 75 +%%EndComments +%%BeginProlog +/languagelevel where +{ pop languagelevel } { 1 } ifelse +2 lt { /Helvetica findfont 12 scalefont setfont 50 500 moveto + (This print job requires a PostScript Language Level 2 printer.) show + showpage quit } if +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +/cairo_set_page_size { + % Change paper size, but only if different from previous paper size otherwise + % duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size + % so we use the same when checking if the size changes. + /setpagedevice where { + pop currentpagedevice + /PageSize known { + 2 copy + currentpagedevice /PageSize get aload pop + exch 4 1 roll + sub abs 5 gt + 3 1 roll + sub abs 5 gt + or + } { + true + } ifelse + { + 2 array astore + 2 dict begin + /PageSize exch def + /ImagingBBox null def + currentdict end + setpagedevice + } { + pop pop + } ifelse + } { + pop + } ifelse +} def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageMedia: 26x26mm +%%PageBoundingBox: 0 0 75 75 +75 75 cairo_set_page_size +%%EndPageSetup +q 0 0 75 75 rectclip +1 0 0 -1 0 75 cm q +0 0 1 rg +0 0 75 75 rectfill +Q Q +showpage +%%Trailer +%%EOF diff --git a/testfiles/cli_tests/testcases/export-id_export-id-only_expected.svg b/testfiles/cli_tests/testcases/export-id_export-id-only_expected.svg new file mode 100644 index 0000000..138e29c --- /dev/null +++ b/testfiles/cli_tests/testcases/export-id_export-id-only_expected.svg @@ -0,0 +1,47 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/testfiles/cli_tests/testcases/export-id_export-id-only_expected.wmf b/testfiles/cli_tests/testcases/export-id_export-id-only_expected.wmf new file mode 100644 index 0000000..25ff1b5 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-id_export-id-only_expected.wmf differ diff --git a/testfiles/cli_tests/testcases/export-id_export-id-only_export-area-drawing_expected.emf b/testfiles/cli_tests/testcases/export-id_export-id-only_export-area-drawing_expected.emf new file mode 100644 index 0000000..e571242 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-id_export-id-only_export-area-drawing_expected.emf differ diff --git a/testfiles/cli_tests/testcases/export-id_export-id-only_export-area-drawing_expected.eps b/testfiles/cli_tests/testcases/export-id_export-id-only_export-area-drawing_expected.eps new file mode 100644 index 0000000..ba306eb --- /dev/null +++ b/testfiles/cli_tests/testcases/export-id_export-id-only_export-area-drawing_expected.eps @@ -0,0 +1,80 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: cairo 1.16.0 (https://cairographics.org) +%%CreationDate: Sat Apr 4 19:16:30 2020 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%BoundingBox: 0 0 225 225 +%%EndComments +%%BeginProlog +50 dict begin +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageBoundingBox: 0 0 225 225 +%%EndPageSetup +q 0 0 75 75 rectclip +1 0 0 -1 0 225 cm q +1 1 0 rg +0 150 75 75 re f +Q Q +showpage +%%Trailer +end +%%EOF diff --git a/testfiles/cli_tests/testcases/export-id_export-id-only_export-area-drawing_expected.pdf b/testfiles/cli_tests/testcases/export-id_export-id-only_export-area-drawing_expected.pdf new file mode 100644 index 0000000..36dd312 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-id_export-id-only_export-area-drawing_expected.pdf differ diff --git a/testfiles/cli_tests/testcases/export-id_export-id-only_export-area-drawing_expected.png b/testfiles/cli_tests/testcases/export-id_export-id-only_export-area-drawing_expected.png new file mode 100644 index 0000000..d27c855 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-id_export-id-only_export-area-drawing_expected.png differ diff --git a/testfiles/cli_tests/testcases/export-id_export-id-only_export-area-drawing_expected.ps b/testfiles/cli_tests/testcases/export-id_export-id-only_export-area-drawing_expected.ps new file mode 100644 index 0000000..fc92a46 --- /dev/null +++ b/testfiles/cli_tests/testcases/export-id_export-id-only_export-area-drawing_expected.ps @@ -0,0 +1,117 @@ +%!PS-Adobe-3.0 +%%Creator: cairo 1.16.0 (https://cairographics.org) +%%CreationDate: Sat Apr 4 19:16:22 2020 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%DocumentMedia: 79x79mm 225 225 0 () () +%%BoundingBox: 0 0 75 75 +%%EndComments +%%BeginProlog +/languagelevel where +{ pop languagelevel } { 1 } ifelse +2 lt { /Helvetica findfont 12 scalefont setfont 50 500 moveto + (This print job requires a PostScript Language Level 2 printer.) show + showpage quit } if +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +/cairo_set_page_size { + % Change paper size, but only if different from previous paper size otherwise + % duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size + % so we use the same when checking if the size changes. + /setpagedevice where { + pop currentpagedevice + /PageSize known { + 2 copy + currentpagedevice /PageSize get aload pop + exch 4 1 roll + sub abs 5 gt + 3 1 roll + sub abs 5 gt + or + } { + true + } ifelse + { + 2 array astore + 2 dict begin + /PageSize exch def + /ImagingBBox null def + currentdict end + setpagedevice + } { + pop pop + } ifelse + } { + pop + } ifelse +} def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageMedia: 79x79mm +%%PageBoundingBox: 0 0 75 75 +225 225 cairo_set_page_size +%%EndPageSetup +q 0 0 75 75 rectclip +1 0 0 -1 0 225 cm q +1 1 0 rg +0 150 75 75 re f +Q Q +showpage +%%Trailer +%%EOF diff --git a/testfiles/cli_tests/testcases/export-id_export-id-only_export-area-drawing_expected.svg b/testfiles/cli_tests/testcases/export-id_export-id-only_export-area-drawing_expected.svg new file mode 100644 index 0000000..d0cd190 --- /dev/null +++ b/testfiles/cli_tests/testcases/export-id_export-id-only_export-area-drawing_expected.svg @@ -0,0 +1,47 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/testfiles/cli_tests/testcases/export-id_export-id-only_export-area-drawing_expected.wmf b/testfiles/cli_tests/testcases/export-id_export-id-only_export-area-drawing_expected.wmf new file mode 100644 index 0000000..9d55069 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-id_export-id-only_export-area-drawing_expected.wmf differ diff --git a/testfiles/cli_tests/testcases/export-id_export-id-only_export-area-page_expected.emf b/testfiles/cli_tests/testcases/export-id_export-id-only_export-area-page_expected.emf new file mode 100644 index 0000000..c27c0c6 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-id_export-id-only_export-area-page_expected.emf differ diff --git a/testfiles/cli_tests/testcases/export-id_export-id-only_export-area-page_expected.eps b/testfiles/cli_tests/testcases/export-id_export-id-only_export-area-page_expected.eps new file mode 100644 index 0000000..8a8bf40 --- /dev/null +++ b/testfiles/cli_tests/testcases/export-id_export-id-only_export-area-page_expected.eps @@ -0,0 +1,80 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: cairo 1.15.10 (http://cairographics.org) +%%CreationDate: Sat Apr 4 18:19:47 2020 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%BoundingBox: 0 0 300 300 +%%EndComments +%%BeginProlog +50 dict begin +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageBoundingBox: 0 0 300 300 +%%EndPageSetup +q 37 37 76 76 rectclip +1 0 0 -1 0 300 cm q +1 1 0 rg +37.5 187.5 75 75 re f +Q Q +showpage +%%Trailer +end +%%EOF diff --git a/testfiles/cli_tests/testcases/export-id_export-id-only_export-area-page_expected.pdf b/testfiles/cli_tests/testcases/export-id_export-id-only_export-area-page_expected.pdf new file mode 100644 index 0000000..b3220d8 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-id_export-id-only_export-area-page_expected.pdf differ diff --git a/testfiles/cli_tests/testcases/export-id_export-id-only_export-area-page_expected.png b/testfiles/cli_tests/testcases/export-id_export-id-only_export-area-page_expected.png new file mode 100644 index 0000000..906dc81 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-id_export-id-only_export-area-page_expected.png differ diff --git a/testfiles/cli_tests/testcases/export-id_export-id-only_export-area-page_expected.ps b/testfiles/cli_tests/testcases/export-id_export-id-only_export-area-page_expected.ps new file mode 100644 index 0000000..b2ab92e --- /dev/null +++ b/testfiles/cli_tests/testcases/export-id_export-id-only_export-area-page_expected.ps @@ -0,0 +1,117 @@ +%!PS-Adobe-3.0 +%%Creator: cairo 1.15.10 (http://cairographics.org) +%%CreationDate: Sat Apr 4 18:19:46 2020 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%DocumentMedia: 106x106mm 300 300 0 () () +%%BoundingBox: 37 37 113 113 +%%EndComments +%%BeginProlog +/languagelevel where +{ pop languagelevel } { 1 } ifelse +2 lt { /Helvetica findfont 12 scalefont setfont 50 500 moveto + (This print job requires a PostScript Language Level 2 printer.) show + showpage quit } if +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +/cairo_set_page_size { + % Change paper size, but only if different from previous paper size otherwise + % duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size + % so we use the same when checking if the size changes. + /setpagedevice where { + pop currentpagedevice + /PageSize known { + 2 copy + currentpagedevice /PageSize get aload pop + exch 4 1 roll + sub abs 5 gt + 3 1 roll + sub abs 5 gt + or + } { + true + } ifelse + { + 2 array astore + 2 dict begin + /PageSize exch def + /ImagingBBox null def + currentdict end + setpagedevice + } { + pop pop + } ifelse + } { + pop + } ifelse +} def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageMedia: 106x106mm +%%PageBoundingBox: 37 37 113 113 +300 300 cairo_set_page_size +%%EndPageSetup +q 37 37 76 76 rectclip +1 0 0 -1 0 300 cm q +1 1 0 rg +37.5 187.5 75 75 re f +Q Q +showpage +%%Trailer +%%EOF diff --git a/testfiles/cli_tests/testcases/export-id_export-id-only_export-area-page_expected.svg b/testfiles/cli_tests/testcases/export-id_export-id-only_export-area-page_expected.svg new file mode 100644 index 0000000..ed5eb1c --- /dev/null +++ b/testfiles/cli_tests/testcases/export-id_export-id-only_export-area-page_expected.svg @@ -0,0 +1,47 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/testfiles/cli_tests/testcases/export-id_export-id-only_export-area-page_expected.wmf b/testfiles/cli_tests/testcases/export-id_export-id-only_export-area-page_expected.wmf new file mode 100644 index 0000000..7b55707 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-id_export-id-only_export-area-page_expected.wmf differ diff --git a/testfiles/cli_tests/testcases/export-ignore-filters_expected.emf b/testfiles/cli_tests/testcases/export-ignore-filters_expected.emf new file mode 100644 index 0000000..44df006 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-ignore-filters_expected.emf differ diff --git a/testfiles/cli_tests/testcases/export-ignore-filters_expected.eps b/testfiles/cli_tests/testcases/export-ignore-filters_expected.eps new file mode 100644 index 0000000..c73abd7 --- /dev/null +++ b/testfiles/cli_tests/testcases/export-ignore-filters_expected.eps @@ -0,0 +1,82 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: cairo 1.15.10 (http://cairographics.org) +%%CreationDate: Fri Apr 10 23:27:45 2020 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%BoundingBox: 0 0 150 75 +%%EndComments +%%BeginProlog +50 dict begin +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageBoundingBox: 0 0 150 75 +%%EndPageSetup +q 0 0 75 75 rectclip +1 0 0 -1 0 75 cm q +1 0 0 rg +0 0 75 75 re f +0 0 1 rg +0 0 75 75 re f +Q Q +showpage +%%Trailer +end +%%EOF diff --git a/testfiles/cli_tests/testcases/export-ignore-filters_expected.pdf b/testfiles/cli_tests/testcases/export-ignore-filters_expected.pdf new file mode 100644 index 0000000..5638e26 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-ignore-filters_expected.pdf differ diff --git a/testfiles/cli_tests/testcases/export-ignore-filters_expected.png b/testfiles/cli_tests/testcases/export-ignore-filters_expected.png new file mode 100644 index 0000000..944611f Binary files /dev/null and b/testfiles/cli_tests/testcases/export-ignore-filters_expected.png differ diff --git a/testfiles/cli_tests/testcases/export-ignore-filters_expected.ps b/testfiles/cli_tests/testcases/export-ignore-filters_expected.ps new file mode 100644 index 0000000..22eb9dc --- /dev/null +++ b/testfiles/cli_tests/testcases/export-ignore-filters_expected.ps @@ -0,0 +1,119 @@ +%!PS-Adobe-3.0 +%%Creator: cairo 1.15.10 (http://cairographics.org) +%%CreationDate: Fri Apr 10 23:27:45 2020 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%DocumentMedia: 53x26mm 150 75 0 () () +%%BoundingBox: 0 0 75 75 +%%EndComments +%%BeginProlog +/languagelevel where +{ pop languagelevel } { 1 } ifelse +2 lt { /Helvetica findfont 12 scalefont setfont 50 500 moveto + (This print job requires a PostScript Language Level 2 printer.) show + showpage quit } if +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +/cairo_set_page_size { + % Change paper size, but only if different from previous paper size otherwise + % duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size + % so we use the same when checking if the size changes. + /setpagedevice where { + pop currentpagedevice + /PageSize known { + 2 copy + currentpagedevice /PageSize get aload pop + exch 4 1 roll + sub abs 5 gt + 3 1 roll + sub abs 5 gt + or + } { + true + } ifelse + { + 2 array astore + 2 dict begin + /PageSize exch def + /ImagingBBox null def + currentdict end + setpagedevice + } { + pop pop + } ifelse + } { + pop + } ifelse +} def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageMedia: 53x26mm +%%PageBoundingBox: 0 0 75 75 +150 75 cairo_set_page_size +%%EndPageSetup +q 0 0 75 75 rectclip +1 0 0 -1 0 75 cm q +1 0 0 rg +0 0 75 75 re f +0 0 1 rg +0 0 75 75 re f +Q Q +showpage +%%Trailer +%%EOF diff --git a/testfiles/cli_tests/testcases/export-ignore-filters_expected.svg b/testfiles/cli_tests/testcases/export-ignore-filters_expected.svg new file mode 100644 index 0000000..a409973 --- /dev/null +++ b/testfiles/cli_tests/testcases/export-ignore-filters_expected.svg @@ -0,0 +1,54 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/testfiles/cli_tests/testcases/export-ignore-filters_expected.wmf b/testfiles/cli_tests/testcases/export-ignore-filters_expected.wmf new file mode 100644 index 0000000..3d64bdf Binary files /dev/null and b/testfiles/cli_tests/testcases/export-ignore-filters_expected.wmf differ diff --git a/testfiles/cli_tests/testcases/export-margin_drawing_expected.emf b/testfiles/cli_tests/testcases/export-margin_drawing_expected.emf new file mode 100644 index 0000000..9e23657 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-margin_drawing_expected.emf differ diff --git a/testfiles/cli_tests/testcases/export-margin_drawing_expected.eps b/testfiles/cli_tests/testcases/export-margin_drawing_expected.eps new file mode 100644 index 0000000..3029a52 --- /dev/null +++ b/testfiles/cli_tests/testcases/export-margin_drawing_expected.eps @@ -0,0 +1,82 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: cairo 1.15.10 (http://cairographics.org) +%%CreationDate: Fri Mar 20 19:25:06 2020 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%BoundingBox: 0 0 150 150 +%%EndComments +%%BeginProlog +50 dict begin +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageBoundingBox: 0 0 150 150 +%%EndPageSetup +q 37 37 76 76 rectclip +1 0 0 -1 0 150 cm q +0 0 1 rg +37.5 37.5 37.5 75 re f +1 0 0 rg +75 37.5 37.5 75 re f +Q Q +showpage +%%Trailer +end +%%EOF diff --git a/testfiles/cli_tests/testcases/export-margin_drawing_expected.pdf b/testfiles/cli_tests/testcases/export-margin_drawing_expected.pdf new file mode 100644 index 0000000..0535214 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-margin_drawing_expected.pdf differ diff --git a/testfiles/cli_tests/testcases/export-margin_drawing_expected.png b/testfiles/cli_tests/testcases/export-margin_drawing_expected.png new file mode 100644 index 0000000..de4aeba Binary files /dev/null and b/testfiles/cli_tests/testcases/export-margin_drawing_expected.png differ diff --git a/testfiles/cli_tests/testcases/export-margin_drawing_expected.ps b/testfiles/cli_tests/testcases/export-margin_drawing_expected.ps new file mode 100644 index 0000000..025c45c --- /dev/null +++ b/testfiles/cli_tests/testcases/export-margin_drawing_expected.ps @@ -0,0 +1,119 @@ +%!PS-Adobe-3.0 +%%Creator: cairo 1.15.10 (http://cairographics.org) +%%CreationDate: Fri Mar 20 19:25:05 2020 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%DocumentMedia: 53x53mm 150 150 0 () () +%%BoundingBox: 37 37 113 113 +%%EndComments +%%BeginProlog +/languagelevel where +{ pop languagelevel } { 1 } ifelse +2 lt { /Helvetica findfont 12 scalefont setfont 50 500 moveto + (This print job requires a PostScript Language Level 2 printer.) show + showpage quit } if +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +/cairo_set_page_size { + % Change paper size, but only if different from previous paper size otherwise + % duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size + % so we use the same when checking if the size changes. + /setpagedevice where { + pop currentpagedevice + /PageSize known { + 2 copy + currentpagedevice /PageSize get aload pop + exch 4 1 roll + sub abs 5 gt + 3 1 roll + sub abs 5 gt + or + } { + true + } ifelse + { + 2 array astore + 2 dict begin + /PageSize exch def + /ImagingBBox null def + currentdict end + setpagedevice + } { + pop pop + } ifelse + } { + pop + } ifelse +} def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageMedia: 53x53mm +%%PageBoundingBox: 37 37 113 113 +150 150 cairo_set_page_size +%%EndPageSetup +q 37 37 76 76 rectclip +1 0 0 -1 0 150 cm q +0 0 1 rg +37.5 37.5 37.5 75 re f +1 0 0 rg +75 37.5 37.5 75 re f +Q Q +showpage +%%Trailer +%%EOF diff --git a/testfiles/cli_tests/testcases/export-margin_drawing_expected.svg b/testfiles/cli_tests/testcases/export-margin_drawing_expected.svg new file mode 100644 index 0000000..e22c4ef --- /dev/null +++ b/testfiles/cli_tests/testcases/export-margin_drawing_expected.svg @@ -0,0 +1,58 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/testfiles/cli_tests/testcases/export-margin_drawing_expected.wmf b/testfiles/cli_tests/testcases/export-margin_drawing_expected.wmf new file mode 100644 index 0000000..d37195f Binary files /dev/null and b/testfiles/cli_tests/testcases/export-margin_drawing_expected.wmf differ diff --git a/testfiles/cli_tests/testcases/export-margin_export-area_expected.png b/testfiles/cli_tests/testcases/export-margin_export-area_expected.png new file mode 100644 index 0000000..2e53613 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-margin_export-area_expected.png differ diff --git a/testfiles/cli_tests/testcases/export-margin_export-id_expected.emf b/testfiles/cli_tests/testcases/export-margin_export-id_expected.emf new file mode 100644 index 0000000..3802d6d Binary files /dev/null and b/testfiles/cli_tests/testcases/export-margin_export-id_expected.emf differ diff --git a/testfiles/cli_tests/testcases/export-margin_export-id_expected.eps b/testfiles/cli_tests/testcases/export-margin_export-id_expected.eps new file mode 100644 index 0000000..6f5f368 --- /dev/null +++ b/testfiles/cli_tests/testcases/export-margin_export-id_expected.eps @@ -0,0 +1,82 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: cairo 1.16.0 (https://cairographics.org) +%%CreationDate: Fri Mar 20 20:03:34 2020 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%BoundingBox: 0 0 113 150 +%%EndComments +%%BeginProlog +50 dict begin +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageBoundingBox: 0 0 113 150 +%%EndPageSetup +q 0 37 75 76 rectclip +1 0 0 -1 0 150 cm q +0 0 1 rg +0 37.5 37.5 75 re f +1 0 0 rg +37.5 37.5 37.5 75 re f +Q Q +showpage +%%Trailer +end +%%EOF diff --git a/testfiles/cli_tests/testcases/export-margin_export-id_expected.pdf b/testfiles/cli_tests/testcases/export-margin_export-id_expected.pdf new file mode 100644 index 0000000..3f2adf7 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-margin_export-id_expected.pdf differ diff --git a/testfiles/cli_tests/testcases/export-margin_export-id_expected.png b/testfiles/cli_tests/testcases/export-margin_export-id_expected.png new file mode 100644 index 0000000..d044884 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-margin_export-id_expected.png differ diff --git a/testfiles/cli_tests/testcases/export-margin_export-id_expected.ps b/testfiles/cli_tests/testcases/export-margin_export-id_expected.ps new file mode 100644 index 0000000..47be99f --- /dev/null +++ b/testfiles/cli_tests/testcases/export-margin_export-id_expected.ps @@ -0,0 +1,119 @@ +%!PS-Adobe-3.0 +%%Creator: cairo 1.16.0 (https://cairographics.org) +%%CreationDate: Fri Mar 20 20:03:12 2020 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%DocumentMedia: 40x53mm 113 150 0 () () +%%BoundingBox: 0 37 75 113 +%%EndComments +%%BeginProlog +/languagelevel where +{ pop languagelevel } { 1 } ifelse +2 lt { /Helvetica findfont 12 scalefont setfont 50 500 moveto + (This print job requires a PostScript Language Level 2 printer.) show + showpage quit } if +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +/cairo_set_page_size { + % Change paper size, but only if different from previous paper size otherwise + % duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size + % so we use the same when checking if the size changes. + /setpagedevice where { + pop currentpagedevice + /PageSize known { + 2 copy + currentpagedevice /PageSize get aload pop + exch 4 1 roll + sub abs 5 gt + 3 1 roll + sub abs 5 gt + or + } { + true + } ifelse + { + 2 array astore + 2 dict begin + /PageSize exch def + /ImagingBBox null def + currentdict end + setpagedevice + } { + pop pop + } ifelse + } { + pop + } ifelse +} def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageMedia: 40x53mm +%%PageBoundingBox: 0 37 75 113 +113 150 cairo_set_page_size +%%EndPageSetup +q 0 37 75 76 rectclip +1 0 0 -1 0 150 cm q +0 0 1 rg +0 37.5 37.5 75 re f +1 0 0 rg +37.5 37.5 37.5 75 re f +Q Q +showpage +%%Trailer +%%EOF diff --git a/testfiles/cli_tests/testcases/export-margin_export-id_expected.svg b/testfiles/cli_tests/testcases/export-margin_export-id_expected.svg new file mode 100644 index 0000000..76e4eb2 --- /dev/null +++ b/testfiles/cli_tests/testcases/export-margin_export-id_expected.svg @@ -0,0 +1,58 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/testfiles/cli_tests/testcases/export-margin_export-id_expected.wmf b/testfiles/cli_tests/testcases/export-margin_export-id_expected.wmf new file mode 100644 index 0000000..407efb7 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-margin_export-id_expected.wmf differ diff --git a/testfiles/cli_tests/testcases/export-margin_export-id_export-id-only_expected.emf b/testfiles/cli_tests/testcases/export-margin_export-id_export-id-only_expected.emf new file mode 100644 index 0000000..5dbda47 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-margin_export-id_export-id-only_expected.emf differ diff --git a/testfiles/cli_tests/testcases/export-margin_export-id_export-id-only_expected.eps b/testfiles/cli_tests/testcases/export-margin_export-id_export-id-only_expected.eps new file mode 100644 index 0000000..b9ac1df --- /dev/null +++ b/testfiles/cli_tests/testcases/export-margin_export-id_export-id-only_expected.eps @@ -0,0 +1,80 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: cairo 1.15.10 (http://cairographics.org) +%%CreationDate: Fri Mar 20 19:25:02 2020 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%BoundingBox: 0 0 113 150 +%%EndComments +%%BeginProlog +50 dict begin +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageBoundingBox: 0 0 113 150 +%%EndPageSetup +q 37 37 38 76 rectclip +1 0 0 -1 0 150 cm q +1 0 0 rg +37.5 37.5 37.5 75 re f +Q Q +showpage +%%Trailer +end +%%EOF diff --git a/testfiles/cli_tests/testcases/export-margin_export-id_export-id-only_expected.pdf b/testfiles/cli_tests/testcases/export-margin_export-id_export-id-only_expected.pdf new file mode 100644 index 0000000..736e623 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-margin_export-id_export-id-only_expected.pdf differ diff --git a/testfiles/cli_tests/testcases/export-margin_export-id_export-id-only_expected.png b/testfiles/cli_tests/testcases/export-margin_export-id_export-id-only_expected.png new file mode 100644 index 0000000..a63f9c4 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-margin_export-id_export-id-only_expected.png differ diff --git a/testfiles/cli_tests/testcases/export-margin_export-id_export-id-only_expected.ps b/testfiles/cli_tests/testcases/export-margin_export-id_export-id-only_expected.ps new file mode 100644 index 0000000..1512864 --- /dev/null +++ b/testfiles/cli_tests/testcases/export-margin_export-id_export-id-only_expected.ps @@ -0,0 +1,117 @@ +%!PS-Adobe-3.0 +%%Creator: cairo 1.15.10 (http://cairographics.org) +%%CreationDate: Fri Mar 20 19:25:01 2020 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%DocumentMedia: 40x53mm 113 150 0 () () +%%BoundingBox: 37 37 75 113 +%%EndComments +%%BeginProlog +/languagelevel where +{ pop languagelevel } { 1 } ifelse +2 lt { /Helvetica findfont 12 scalefont setfont 50 500 moveto + (This print job requires a PostScript Language Level 2 printer.) show + showpage quit } if +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +/cairo_set_page_size { + % Change paper size, but only if different from previous paper size otherwise + % duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size + % so we use the same when checking if the size changes. + /setpagedevice where { + pop currentpagedevice + /PageSize known { + 2 copy + currentpagedevice /PageSize get aload pop + exch 4 1 roll + sub abs 5 gt + 3 1 roll + sub abs 5 gt + or + } { + true + } ifelse + { + 2 array astore + 2 dict begin + /PageSize exch def + /ImagingBBox null def + currentdict end + setpagedevice + } { + pop pop + } ifelse + } { + pop + } ifelse +} def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageMedia: 40x53mm +%%PageBoundingBox: 37 37 75 113 +113 150 cairo_set_page_size +%%EndPageSetup +q 37 37 38 76 rectclip +1 0 0 -1 0 150 cm q +1 0 0 rg +37.5 37.5 37.5 75 re f +Q Q +showpage +%%Trailer +%%EOF diff --git a/testfiles/cli_tests/testcases/export-margin_export-id_export-id-only_expected.svg b/testfiles/cli_tests/testcases/export-margin_export-id_export-id-only_expected.svg new file mode 100644 index 0000000..0d1c2ac --- /dev/null +++ b/testfiles/cli_tests/testcases/export-margin_export-id_export-id-only_expected.svg @@ -0,0 +1,51 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/testfiles/cli_tests/testcases/export-margin_export-id_export-id-only_expected.wmf b/testfiles/cli_tests/testcases/export-margin_export-id_export-id-only_expected.wmf new file mode 100644 index 0000000..169b86d Binary files /dev/null and b/testfiles/cli_tests/testcases/export-margin_export-id_export-id-only_expected.wmf differ diff --git a/testfiles/cli_tests/testcases/export-margin_mm_expected.emf b/testfiles/cli_tests/testcases/export-margin_mm_expected.emf new file mode 100644 index 0000000..78c3182 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-margin_mm_expected.emf differ diff --git a/testfiles/cli_tests/testcases/export-margin_mm_expected.eps b/testfiles/cli_tests/testcases/export-margin_mm_expected.eps new file mode 100644 index 0000000..fba673e --- /dev/null +++ b/testfiles/cli_tests/testcases/export-margin_mm_expected.eps @@ -0,0 +1,82 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: cairo 1.15.10 (http://cairographics.org) +%%CreationDate: Fri Mar 20 19:24:54 2020 +%%Pages: 1 +%%BoundingBox: 0 0 851 851 +%%HiResBoundingBox: 0 0 851 851 +%%LanguageLevel: 3 +%%EndComments +%%BeginProlog +50 dict begin +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageBoundingBox: 0 0 851 851 +%%EndPageSetup +q 283 284 284 284 rectclip +1 0 0 -1 0 851 cm q +0 0 1 rg +283.465 283.465 141.73 283.465 re f +1 0 0 rg +425.195 283.465 141.734 283.465 re f +Q Q +showpage +%%Trailer +end +%%EOF diff --git a/testfiles/cli_tests/testcases/export-margin_mm_expected.pdf b/testfiles/cli_tests/testcases/export-margin_mm_expected.pdf new file mode 100644 index 0000000..f4c472c Binary files /dev/null and b/testfiles/cli_tests/testcases/export-margin_mm_expected.pdf differ diff --git a/testfiles/cli_tests/testcases/export-margin_mm_expected.png b/testfiles/cli_tests/testcases/export-margin_mm_expected.png new file mode 100644 index 0000000..9c22341 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-margin_mm_expected.png differ diff --git a/testfiles/cli_tests/testcases/export-margin_mm_expected.ps b/testfiles/cli_tests/testcases/export-margin_mm_expected.ps new file mode 100644 index 0000000..abb8818 --- /dev/null +++ b/testfiles/cli_tests/testcases/export-margin_mm_expected.ps @@ -0,0 +1,119 @@ +%!PS-Adobe-3.0 +%%Creator: cairo 1.15.10 (http://cairographics.org) +%%CreationDate: Fri Mar 20 19:24:53 2020 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%DocumentMedia: 300x300mm 850 850 0 () () +%%BoundingBox: 283 284 567 568 +%%EndComments +%%BeginProlog +/languagelevel where +{ pop languagelevel } { 1 } ifelse +2 lt { /Helvetica findfont 12 scalefont setfont 50 500 moveto + (This print job requires a PostScript Language Level 2 printer.) show + showpage quit } if +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +/cairo_set_page_size { + % Change paper size, but only if different from previous paper size otherwise + % duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size + % so we use the same when checking if the size changes. + /setpagedevice where { + pop currentpagedevice + /PageSize known { + 2 copy + currentpagedevice /PageSize get aload pop + exch 4 1 roll + sub abs 5 gt + 3 1 roll + sub abs 5 gt + or + } { + true + } ifelse + { + 2 array astore + 2 dict begin + /PageSize exch def + /ImagingBBox null def + currentdict end + setpagedevice + } { + pop pop + } ifelse + } { + pop + } ifelse +} def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageMedia: 300x300mm +%%PageBoundingBox: 283 284 567 568 +851 851 cairo_set_page_size +%%EndPageSetup +q 283 284 284 284 rectclip +1 0 0 -1 0 851 cm q +0 0 1 rg +283.465 283.465 141.73 283.465 re f +1 0 0 rg +425.195 283.465 141.734 283.465 re f +Q Q +showpage +%%Trailer +%%EOF diff --git a/testfiles/cli_tests/testcases/export-margin_mm_expected.svg b/testfiles/cli_tests/testcases/export-margin_mm_expected.svg new file mode 100644 index 0000000..424e8f8 --- /dev/null +++ b/testfiles/cli_tests/testcases/export-margin_mm_expected.svg @@ -0,0 +1,58 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/testfiles/cli_tests/testcases/export-margin_mm_expected.wmf b/testfiles/cli_tests/testcases/export-margin_mm_expected.wmf new file mode 100644 index 0000000..0131399 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-margin_mm_expected.wmf differ diff --git a/testfiles/cli_tests/testcases/export-margin_mm_viewbox_drawing_expected.emf b/testfiles/cli_tests/testcases/export-margin_mm_viewbox_drawing_expected.emf new file mode 100644 index 0000000..e0130bf Binary files /dev/null and b/testfiles/cli_tests/testcases/export-margin_mm_viewbox_drawing_expected.emf differ diff --git a/testfiles/cli_tests/testcases/export-margin_mm_viewbox_drawing_expected.eps b/testfiles/cli_tests/testcases/export-margin_mm_viewbox_drawing_expected.eps new file mode 100644 index 0000000..7975c1d --- /dev/null +++ b/testfiles/cli_tests/testcases/export-margin_mm_viewbox_drawing_expected.eps @@ -0,0 +1,82 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: cairo 1.16.0 (https://cairographics.org) +%%CreationDate: Sat Mar 21 23:14:25 2020 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%BoundingBox: 0 0 567 567 +%%EndComments +%%BeginProlog +50 dict begin +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageBoundingBox: 0 0 567 567 +%%EndPageSetup +q 141 141 285 285 rectclip +1 0 0 -1 0 567 cm q +0 0 1 rg +141.73 141.73 141.734 283.465 re f +1 0 0 rg +283.465 141.73 141.73 283.465 re f +Q Q +showpage +%%Trailer +end +%%EOF diff --git a/testfiles/cli_tests/testcases/export-margin_mm_viewbox_drawing_expected.pdf b/testfiles/cli_tests/testcases/export-margin_mm_viewbox_drawing_expected.pdf new file mode 100644 index 0000000..a463c55 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-margin_mm_viewbox_drawing_expected.pdf differ diff --git a/testfiles/cli_tests/testcases/export-margin_mm_viewbox_drawing_expected.png b/testfiles/cli_tests/testcases/export-margin_mm_viewbox_drawing_expected.png new file mode 100644 index 0000000..84fd7bc Binary files /dev/null and b/testfiles/cli_tests/testcases/export-margin_mm_viewbox_drawing_expected.png differ diff --git a/testfiles/cli_tests/testcases/export-margin_mm_viewbox_drawing_expected.ps b/testfiles/cli_tests/testcases/export-margin_mm_viewbox_drawing_expected.ps new file mode 100644 index 0000000..d43e2b3 --- /dev/null +++ b/testfiles/cli_tests/testcases/export-margin_mm_viewbox_drawing_expected.ps @@ -0,0 +1,119 @@ +%!PS-Adobe-3.0 +%%Creator: cairo 1.16.0 (https://cairographics.org) +%%CreationDate: Sat Mar 21 23:14:19 2020 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%DocumentMedia: 200x200mm 567 567 0 () () +%%BoundingBox: 141 141 426 426 +%%EndComments +%%BeginProlog +/languagelevel where +{ pop languagelevel } { 1 } ifelse +2 lt { /Helvetica findfont 12 scalefont setfont 50 500 moveto + (This print job requires a PostScript Language Level 2 printer.) show + showpage quit } if +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +/cairo_set_page_size { + % Change paper size, but only if different from previous paper size otherwise + % duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size + % so we use the same when checking if the size changes. + /setpagedevice where { + pop currentpagedevice + /PageSize known { + 2 copy + currentpagedevice /PageSize get aload pop + exch 4 1 roll + sub abs 5 gt + 3 1 roll + sub abs 5 gt + or + } { + true + } ifelse + { + 2 array astore + 2 dict begin + /PageSize exch def + /ImagingBBox null def + currentdict end + setpagedevice + } { + pop pop + } ifelse + } { + pop + } ifelse +} def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageMedia: 200x200mm +%%PageBoundingBox: 141 141 426 426 +567 567 cairo_set_page_size +%%EndPageSetup +q 141 141 285 285 rectclip +1 0 0 -1 0 567 cm q +0 0 1 rg +141.73 141.73 141.734 283.465 re f +1 0 0 rg +283.465 141.73 141.73 283.465 re f +Q Q +showpage +%%Trailer +%%EOF diff --git a/testfiles/cli_tests/testcases/export-margin_mm_viewbox_drawing_expected.svg b/testfiles/cli_tests/testcases/export-margin_mm_viewbox_drawing_expected.svg new file mode 100644 index 0000000..e7773cd --- /dev/null +++ b/testfiles/cli_tests/testcases/export-margin_mm_viewbox_drawing_expected.svg @@ -0,0 +1,64 @@ + + + + + + image/svg+xml + + + + + + + + + + diff --git a/testfiles/cli_tests/testcases/export-margin_mm_viewbox_drawing_expected.wmf b/testfiles/cli_tests/testcases/export-margin_mm_viewbox_drawing_expected.wmf new file mode 100644 index 0000000..f8c842d Binary files /dev/null and b/testfiles/cli_tests/testcases/export-margin_mm_viewbox_drawing_expected.wmf differ diff --git a/testfiles/cli_tests/testcases/export-margin_mm_viewbox_id_expected.emf b/testfiles/cli_tests/testcases/export-margin_mm_viewbox_id_expected.emf new file mode 100644 index 0000000..6315da5 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-margin_mm_viewbox_id_expected.emf differ diff --git a/testfiles/cli_tests/testcases/export-margin_mm_viewbox_id_expected.eps b/testfiles/cli_tests/testcases/export-margin_mm_viewbox_id_expected.eps new file mode 100644 index 0000000..3d0d510 --- /dev/null +++ b/testfiles/cli_tests/testcases/export-margin_mm_viewbox_id_expected.eps @@ -0,0 +1,82 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: cairo 1.16.0 (https://cairographics.org) +%%CreationDate: Sat Mar 21 23:11:11 2020 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%BoundingBox: 0 0 426 567 +%%EndComments +%%BeginProlog +50 dict begin +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageBoundingBox: 0 0 426 567 +%%EndPageSetup +q 0 141 284 285 rectclip +1 0 0 -1 0 567 cm q +0 0 1 rg +0 141.73 141.73 283.465 re f +1 0 0 rg +141.73 141.73 141.734 283.465 re f +Q Q +showpage +%%Trailer +end +%%EOF diff --git a/testfiles/cli_tests/testcases/export-margin_mm_viewbox_id_expected.pdf b/testfiles/cli_tests/testcases/export-margin_mm_viewbox_id_expected.pdf new file mode 100644 index 0000000..f9914b4 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-margin_mm_viewbox_id_expected.pdf differ diff --git a/testfiles/cli_tests/testcases/export-margin_mm_viewbox_id_expected.png b/testfiles/cli_tests/testcases/export-margin_mm_viewbox_id_expected.png new file mode 100644 index 0000000..51a98e1 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-margin_mm_viewbox_id_expected.png differ diff --git a/testfiles/cli_tests/testcases/export-margin_mm_viewbox_id_expected.ps b/testfiles/cli_tests/testcases/export-margin_mm_viewbox_id_expected.ps new file mode 100644 index 0000000..08192c1 --- /dev/null +++ b/testfiles/cli_tests/testcases/export-margin_mm_viewbox_id_expected.ps @@ -0,0 +1,119 @@ +%!PS-Adobe-3.0 +%%Creator: cairo 1.16.0 (https://cairographics.org) +%%CreationDate: Sat Mar 21 23:11:23 2020 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%DocumentMedia: 150x200mm 425 567 0 () () +%%BoundingBox: 0 141 284 426 +%%EndComments +%%BeginProlog +/languagelevel where +{ pop languagelevel } { 1 } ifelse +2 lt { /Helvetica findfont 12 scalefont setfont 50 500 moveto + (This print job requires a PostScript Language Level 2 printer.) show + showpage quit } if +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +/cairo_set_page_size { + % Change paper size, but only if different from previous paper size otherwise + % duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size + % so we use the same when checking if the size changes. + /setpagedevice where { + pop currentpagedevice + /PageSize known { + 2 copy + currentpagedevice /PageSize get aload pop + exch 4 1 roll + sub abs 5 gt + 3 1 roll + sub abs 5 gt + or + } { + true + } ifelse + { + 2 array astore + 2 dict begin + /PageSize exch def + /ImagingBBox null def + currentdict end + setpagedevice + } { + pop pop + } ifelse + } { + pop + } ifelse +} def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageMedia: 150x200mm +%%PageBoundingBox: 0 141 284 426 +426 567 cairo_set_page_size +%%EndPageSetup +q 0 141 284 285 rectclip +1 0 0 -1 0 567 cm q +0 0 1 rg +0 141.73 141.73 283.465 re f +1 0 0 rg +141.73 141.73 141.734 283.465 re f +Q Q +showpage +%%Trailer +%%EOF diff --git a/testfiles/cli_tests/testcases/export-margin_mm_viewbox_id_expected.svg b/testfiles/cli_tests/testcases/export-margin_mm_viewbox_id_expected.svg new file mode 100644 index 0000000..6e39aa8 --- /dev/null +++ b/testfiles/cli_tests/testcases/export-margin_mm_viewbox_id_expected.svg @@ -0,0 +1,50 @@ + + + + + + + + diff --git a/testfiles/cli_tests/testcases/export-margin_mm_viewbox_id_expected.wmf b/testfiles/cli_tests/testcases/export-margin_mm_viewbox_id_expected.wmf new file mode 100644 index 0000000..18179f4 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-margin_mm_viewbox_id_expected.wmf differ diff --git a/testfiles/cli_tests/testcases/export-margin_mm_viewbox_page_expected.emf b/testfiles/cli_tests/testcases/export-margin_mm_viewbox_page_expected.emf new file mode 100644 index 0000000..78c3182 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-margin_mm_viewbox_page_expected.emf differ diff --git a/testfiles/cli_tests/testcases/export-margin_mm_viewbox_page_expected.eps b/testfiles/cli_tests/testcases/export-margin_mm_viewbox_page_expected.eps new file mode 100644 index 0000000..fba673e --- /dev/null +++ b/testfiles/cli_tests/testcases/export-margin_mm_viewbox_page_expected.eps @@ -0,0 +1,82 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: cairo 1.15.10 (http://cairographics.org) +%%CreationDate: Fri Mar 20 19:24:54 2020 +%%Pages: 1 +%%BoundingBox: 0 0 851 851 +%%HiResBoundingBox: 0 0 851 851 +%%LanguageLevel: 3 +%%EndComments +%%BeginProlog +50 dict begin +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageBoundingBox: 0 0 851 851 +%%EndPageSetup +q 283 284 284 284 rectclip +1 0 0 -1 0 851 cm q +0 0 1 rg +283.465 283.465 141.73 283.465 re f +1 0 0 rg +425.195 283.465 141.734 283.465 re f +Q Q +showpage +%%Trailer +end +%%EOF diff --git a/testfiles/cli_tests/testcases/export-margin_mm_viewbox_page_expected.pdf b/testfiles/cli_tests/testcases/export-margin_mm_viewbox_page_expected.pdf new file mode 100644 index 0000000..f4c472c Binary files /dev/null and b/testfiles/cli_tests/testcases/export-margin_mm_viewbox_page_expected.pdf differ diff --git a/testfiles/cli_tests/testcases/export-margin_mm_viewbox_page_expected.png b/testfiles/cli_tests/testcases/export-margin_mm_viewbox_page_expected.png new file mode 100644 index 0000000..9c22341 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-margin_mm_viewbox_page_expected.png differ diff --git a/testfiles/cli_tests/testcases/export-margin_mm_viewbox_page_expected.ps b/testfiles/cli_tests/testcases/export-margin_mm_viewbox_page_expected.ps new file mode 100644 index 0000000..abb8818 --- /dev/null +++ b/testfiles/cli_tests/testcases/export-margin_mm_viewbox_page_expected.ps @@ -0,0 +1,119 @@ +%!PS-Adobe-3.0 +%%Creator: cairo 1.15.10 (http://cairographics.org) +%%CreationDate: Fri Mar 20 19:24:53 2020 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%DocumentMedia: 300x300mm 850 850 0 () () +%%BoundingBox: 283 284 567 568 +%%EndComments +%%BeginProlog +/languagelevel where +{ pop languagelevel } { 1 } ifelse +2 lt { /Helvetica findfont 12 scalefont setfont 50 500 moveto + (This print job requires a PostScript Language Level 2 printer.) show + showpage quit } if +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +/cairo_set_page_size { + % Change paper size, but only if different from previous paper size otherwise + % duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size + % so we use the same when checking if the size changes. + /setpagedevice where { + pop currentpagedevice + /PageSize known { + 2 copy + currentpagedevice /PageSize get aload pop + exch 4 1 roll + sub abs 5 gt + 3 1 roll + sub abs 5 gt + or + } { + true + } ifelse + { + 2 array astore + 2 dict begin + /PageSize exch def + /ImagingBBox null def + currentdict end + setpagedevice + } { + pop pop + } ifelse + } { + pop + } ifelse +} def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageMedia: 300x300mm +%%PageBoundingBox: 283 284 567 568 +851 851 cairo_set_page_size +%%EndPageSetup +q 283 284 284 284 rectclip +1 0 0 -1 0 851 cm q +0 0 1 rg +283.465 283.465 141.73 283.465 re f +1 0 0 rg +425.195 283.465 141.734 283.465 re f +Q Q +showpage +%%Trailer +%%EOF diff --git a/testfiles/cli_tests/testcases/export-margin_mm_viewbox_page_expected.svg b/testfiles/cli_tests/testcases/export-margin_mm_viewbox_page_expected.svg new file mode 100644 index 0000000..8274392 --- /dev/null +++ b/testfiles/cli_tests/testcases/export-margin_mm_viewbox_page_expected.svg @@ -0,0 +1,64 @@ + + + + + + image/svg+xml + + + + + + + + + + diff --git a/testfiles/cli_tests/testcases/export-margin_mm_viewbox_page_expected.wmf b/testfiles/cli_tests/testcases/export-margin_mm_viewbox_page_expected.wmf new file mode 100644 index 0000000..0131399 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-margin_mm_viewbox_page_expected.wmf differ diff --git a/testfiles/cli_tests/testcases/export-margin_px_expected.emf b/testfiles/cli_tests/testcases/export-margin_px_expected.emf new file mode 100644 index 0000000..0d52e4d Binary files /dev/null and b/testfiles/cli_tests/testcases/export-margin_px_expected.emf differ diff --git a/testfiles/cli_tests/testcases/export-margin_px_expected.eps b/testfiles/cli_tests/testcases/export-margin_px_expected.eps new file mode 100644 index 0000000..dcc1bc0 --- /dev/null +++ b/testfiles/cli_tests/testcases/export-margin_px_expected.eps @@ -0,0 +1,82 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: cairo 1.15.10 (http://cairographics.org) +%%CreationDate: Fri Mar 20 19:24:50 2020 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%BoundingBox: 0 0 225 225 +%%EndComments +%%BeginProlog +50 dict begin +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageBoundingBox: 0 0 225 225 +%%EndPageSetup +q 75 75 75 75 rectclip +1 0 0 -1 0 225 cm q +0 0 1 rg +75 75 37.5 75 re f +1 0 0 rg +112.5 75 37.5 75 re f +Q Q +showpage +%%Trailer +end +%%EOF diff --git a/testfiles/cli_tests/testcases/export-margin_px_expected.pdf b/testfiles/cli_tests/testcases/export-margin_px_expected.pdf new file mode 100644 index 0000000..a467503 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-margin_px_expected.pdf differ diff --git a/testfiles/cli_tests/testcases/export-margin_px_expected.png b/testfiles/cli_tests/testcases/export-margin_px_expected.png new file mode 100644 index 0000000..a9ce6f9 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-margin_px_expected.png differ diff --git a/testfiles/cli_tests/testcases/export-margin_px_expected.ps b/testfiles/cli_tests/testcases/export-margin_px_expected.ps new file mode 100644 index 0000000..c2fa5e8 --- /dev/null +++ b/testfiles/cli_tests/testcases/export-margin_px_expected.ps @@ -0,0 +1,119 @@ +%!PS-Adobe-3.0 +%%Creator: cairo 1.15.10 (http://cairographics.org) +%%CreationDate: Fri Mar 20 19:24:49 2020 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%DocumentMedia: 79x79mm 225 225 0 () () +%%BoundingBox: 75 75 150 150 +%%EndComments +%%BeginProlog +/languagelevel where +{ pop languagelevel } { 1 } ifelse +2 lt { /Helvetica findfont 12 scalefont setfont 50 500 moveto + (This print job requires a PostScript Language Level 2 printer.) show + showpage quit } if +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +/cairo_set_page_size { + % Change paper size, but only if different from previous paper size otherwise + % duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size + % so we use the same when checking if the size changes. + /setpagedevice where { + pop currentpagedevice + /PageSize known { + 2 copy + currentpagedevice /PageSize get aload pop + exch 4 1 roll + sub abs 5 gt + 3 1 roll + sub abs 5 gt + or + } { + true + } ifelse + { + 2 array astore + 2 dict begin + /PageSize exch def + /ImagingBBox null def + currentdict end + setpagedevice + } { + pop pop + } ifelse + } { + pop + } ifelse +} def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageMedia: 79x79mm +%%PageBoundingBox: 75 75 150 150 +225 225 cairo_set_page_size +%%EndPageSetup +q 75 75 75 75 rectclip +1 0 0 -1 0 225 cm q +0 0 1 rg +75 75 37.5 75 re f +1 0 0 rg +112.5 75 37.5 75 re f +Q Q +showpage +%%Trailer +%%EOF diff --git a/testfiles/cli_tests/testcases/export-margin_px_expected.svg b/testfiles/cli_tests/testcases/export-margin_px_expected.svg new file mode 100644 index 0000000..b4ec04f --- /dev/null +++ b/testfiles/cli_tests/testcases/export-margin_px_expected.svg @@ -0,0 +1,58 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/testfiles/cli_tests/testcases/export-margin_px_expected.wmf b/testfiles/cli_tests/testcases/export-margin_px_expected.wmf new file mode 100644 index 0000000..57bd577 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-margin_px_expected.wmf differ diff --git a/testfiles/cli_tests/testcases/export-marker-on-clone.svg b/testfiles/cli_tests/testcases/export-marker-on-clone.svg new file mode 100644 index 0000000..5cd9d1e --- /dev/null +++ b/testfiles/cli_tests/testcases/export-marker-on-clone.svg @@ -0,0 +1,106 @@ + + + + + + This is a simple square marker filled with context-fill and stroked with context-stroke. + + + + + + + A simple path referencing the marker; expected marker fill #ffff00 and stroke #ff0000. + + + A simple clone of a path. Expected fill #ffff00 stroke #ff0000 + + + + + + + + We expect that the marker is rendered with #000080 fill and #008000 stroke. + + + The style set by use should be overriden by g1's style, rendering the marker + with #000080 fill and #008000 stroke. + + + + + + Unset fill defaults to #000000 so we expect the square to have this fill but no stroke. + + + Expecting the brown #503000 stroke to be inherited by the marker but fill should remain #000000. + + + + Fill should now be set to dark blue but still no stroke. + + + + + + + + + + We clone g3 but the styles that g3 inherited should not be copied. + Therefore, expecting black fill (default value) and no stroke. + + + Expecting the shadow DOM host to set fill and stroke on the marker. + + + + + + + + The group should override the style, so the cloned path should not be rendered. + + + + + + Should render with black fill and no stroke. + + + Clone should be rendered with #80ffff fill and still no stroke. + + + Fill should remain at #80ffff but stroke should be #008000. + + diff --git a/testfiles/cli_tests/testcases/export-marker-on-clone_expected.png b/testfiles/cli_tests/testcases/export-marker-on-clone_expected.png new file mode 100644 index 0000000..4dd30ac Binary files /dev/null and b/testfiles/cli_tests/testcases/export-marker-on-clone_expected.png differ diff --git a/testfiles/cli_tests/testcases/export-png-color-mode-gray-8_expected.png b/testfiles/cli_tests/testcases/export-png-color-mode-gray-8_expected.png new file mode 100644 index 0000000..3ee1468 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-png-color-mode-gray-8_expected.png differ diff --git a/testfiles/cli_tests/testcases/export-png-color-mode-rgb-8_expected.png b/testfiles/cli_tests/testcases/export-png-color-mode-rgb-8_expected.png new file mode 100644 index 0000000..0677fba Binary files /dev/null and b/testfiles/cli_tests/testcases/export-png-color-mode-rgb-8_expected.png differ diff --git a/testfiles/cli_tests/testcases/export-png-color-mode-rgba-8_expected.png b/testfiles/cli_tests/testcases/export-png-color-mode-rgba-8_expected.png new file mode 100644 index 0000000..4798382 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-png-color-mode-rgba-8_expected.png differ diff --git a/testfiles/cli_tests/testcases/export-ps-level-2_expected.ps b/testfiles/cli_tests/testcases/export-ps-level-2_expected.ps new file mode 100644 index 0000000..2d73ecc --- /dev/null +++ b/testfiles/cli_tests/testcases/export-ps-level-2_expected.ps @@ -0,0 +1,2366 @@ +%!PS-Adobe-3.0 +%%Creator: cairo 1.15.10 (http://cairographics.org) +%%CreationDate: Tue Apr 7 23:58:52 2020 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%DocumentMedia: 53x53mm 150 150 0 () () +%%BoundingBox: 37 37 113 113 +%%EndComments +%%BeginProlog +/languagelevel where +{ pop languagelevel } { 1 } ifelse +2 lt { /Helvetica findfont 12 scalefont setfont 50 500 moveto + (This print job requires a PostScript Language Level 2 printer.) show + showpage quit } if +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +/cairo_set_page_size { + % Change paper size, but only if different from previous paper size otherwise + % duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size + % so we use the same when checking if the size changes. + /setpagedevice where { + pop currentpagedevice + /PageSize known { + 2 copy + currentpagedevice /PageSize get aload pop + exch 4 1 roll + sub abs 5 gt + 3 1 roll + sub abs 5 gt + or + } { + true + } ifelse + { + 2 array astore + 2 dict begin + /PageSize exch def + /ImagingBBox null def + currentdict end + setpagedevice + } { + pop pop + } ifelse + } { + pop + } ifelse +} def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageMedia: 53x53mm +%%PageBoundingBox: 37 37 113 113 +150 150 cairo_set_page_size +%%EndPageSetup +q 37 37 76 76 rectclip +1 0 0 -1 0 150 cm q +Q q +37 37 76 76 re W n +q +37 37 76 76 re W n +% Fallback Image: x=37 y=37 w=76 h=76 res=300ppi size=301467 +[ 76.08 0 0 -76.08 37 113.08 ] concat +/cairo_ascii85_file currentfile /ASCII85Decode filter def +/DeviceRGB setcolorspace +<< + /ImageType 1 + /Width 317 + /Height 317 + /Interpolate false + /BitsPerComponent 8 + /Decode [ 0 1 0 1 0 1 ] + /DataSource cairo_ascii85_file /LZWDecode filter + /ImageMatrix [ 317 0 0 -317 0 317 ] +>> +cairo_image + J3Vsg3$]7K#D>EP:q1$o*=mro@So+\<\5,H7Uo<*jE<[.O@Wn[3@'nb-^757;Rp>H>q_R=A + lC^cenm@9:1mM9jRso`J%pXIp[&Fc7\i9+F>q_Zo0[n)-uXDNg4o:$d,(%f)pX3/TtG&W$Q + T]66[)]^DV?&8gI$,1h,H=-%FK?m\aM@62%@_rEaU;53ZEs2iM!b934Jd6L>JW5BPkta%C\ + ]A:tn"3j8H5MCXCeb2Qi3te=j85D(T$&UTU;t-uA`!3eko6d+OXA?LiinTsAo"2FS(96Xrs + F]W)naRmUW2fMja>;MMu\YNTce)Y^l$J$189\f0nX6X8!i'K*Q5D=70`f!I<2%;/%2IOJtM + )T%51^Zl\B!@VMoL/o^6>)$$CC)KpCPn8P%;lF7a.\W*,)+^HcC)KGN-tNfRTp,m%58,GUn + qKoD-tI(I7S7D5(F + pnE;MITiCd)Spth^(ar&j+NKjR"25k"_L,@EF(su>3@lf?#-t=` + ]10/-,`hA>k_M0brC)Fa[Ms;.lTo5of\RR$sh3+Ii8f4mI-t:&J/#Zp_L+9-2V6b$2igPTN + -t81QTo'0oStV"`(n5YRL\R71M/!llTnt_&L*S0V3eekdGNo)NW+$s*+fJi_L*5F>(kA<8p + gSt"@Hn\UTneLBG+j[`(itZb@E(@%L?]UmOXOYCD)>jI_Rc4(F++"4%I;=V`\9r + (I^=05+g>VA#*h)'NXr^fE\9R=pL:DHnLN`Qt*P14_XrViIeZKoT7_!PULH`4hCPOPfXrNn + ha0$CciPVf<*9ObLl\>,CDX3&?9E#CMS[-\8#f6T6%.R>2c6Z)B.S, + iG40k\7Y&]B"2FA+L*!j5CPEA;Xr&)9J$'UAiHt65L$#mNl + \3p">jI*al`M$"iGG_5]&@?&Rc(MfK#t/'(:Yn<4'WNfE*"p5>1Wpf>SV3LXpV&9.U(c/CP + &,e[D>Pg"Y3glNGqb&_*n[!i1XqSF5Y:,TjGf*'Q-'/8[R$&5r9g>J94JlX + &&_'mn-XpEUg,9ja)&T)JU28KpW"@QX5JfV!"B"0!fi.Plt%Q1Z + 1"P3ba>S%?`5K?7'+.D,9COu#Y#I_,`U\e:,/Tj*QS!/0[D9H,! + pa)!NGpJn7^s5&i)siH@BKcZRb%VI>RITP.Q,#=f)h2hCOnpW"U;IMg>G_r/T<&E^mN^T>? + hJ5WNHAi\.T_<7r*YnXoqnmf)d1%#]4N(28Fi/Rc$GHJGlVQk-tq*i&m\bE#l@%FOpQ:>R( + s7XoijW%@Z34COjg8"$/Kp!L$am/T;Oa^gB[%i%Aeq[B9SN\-hJI>Qmi@'Xb_Qf)[s<"Ou\ + =!`lf;)W2iUJ;U&.^d?WR"'brqE"=hA4&_U+*,C(AXoYE0#&:bT!tB2W[D4?F!39?+Q&2XE + 2RimYi"9aT6FF5:*nLC5>QLsG"uX14?(0[YCOd_6!/@ualhaoec#<`l^^/Nl*JeK]$*F7Gl + 4I4A6#2,GY;RShf\7ohD.EOP&,+^AgN`a9,Q506p?QF^]6S=3r-u37rnpO!!iE$+7KPc^d7u$L\qFr0Id8lkTL*7r"C1A=F\/R"*,,ru-[jCsFcH3(GC`5NA^BNs#AE&,>oaj7*)1)@Ac$8I + Zl.9T,%=5"Ndms!7pJQW$ds:Cm7^5NMnFPXGFJ&,AqtV*q>KP.Fd,q-lSMT`:=qr`g!0FfV + A"^6fQ@@h8Bm5NY62R7$sc&,Dd"VM*BdP?Mf6q6E6Ii;]:arbrDD`NuHfk+*0iJ'P>!4,%3P4)J'el.7/+A0U@`&WdII6,L]&A< + oSo6dJcA<^r@/o]Y^b*[s%GD#18^;iS5[9(:<3;s+8#V`e0_(j8,Yq"od#%:Z2[`mrD2N47 + ]FpAZ9RWis/TZ/Y)iFn5O/UTXF0<=ep>eL8,[6Got5e+&cZ+VrH04ThkeqnpKq_Bs0)V)_2 + nI#5O:B0Yruj@f\7KY>2*=)m=*7P>AJ + 4r*+8ed_u7Y\l[*Sg*Q>P_-KM`=rof.7Uq-4\l!6r`lgE$M]9dj5?7Wt_8,L%CmfrDjHe3U + _IIY$qc015_VE`q>q7da:s'Q6=JM8>1+7G_L^75pi?Z?$o"9&$+pZ[?ML(qMNpC-#>3JXjD + ,/d;/J&'J3"3:CJ&@e?rSd8GL + ]dW"AF,c-/>e8?XNIWMbA=c5m)$sp[NF_E+6^G_m**^JfZhXuc:m;YiVni\rf1gacKt8NXT + $Qu#1.&g+6a+Skml)p<0;b2Ohq$[!WJ6V*mr4XP9KphD(O,;1=L)$e*T>a+o[a8rfs2VQ0h + 8pX]!\d>1N"HQ_FDFiVo2frg7P=eu`uED0jh$I[VGZRUQ2aV7P<]NnH"Mfr^k!l,m;VVV8/ + NgC:OENP(k-^>(10T8EX)+o\-#rh+iATY^W$%GopcjPGm_h[T(L_>^21rhDdYUQ,91c33'_ + 9&T+GiD(6Kc62*RAnXpf+6mn+mO[Bn39//;j6f75$C%NIT]KV]W^6WaJc;[p@--fgk,rUb/ + p?DmbTH,s+6qA>/qE/GN9Lh:l&M.762n'=riaVCl7T+%m]u.r[]tD_Z,Q^J!ZGiI-F0esCa + &fEmaCK<3jZ!ED?f0>b>9;KkjHac[EDhDB*`Is'Xo7bn1P"8X+%PH&9A(5F3Be&X-iH.S@T + 4\Fba56$afM>G?M5oA^JANiVnROp$6S_YpQ'^BJLj84n#6*/Ykq*n`D='t-p#@VrqoR5r6Na19Q^5H>)"S_+7J.e + :[%f!G5hu:s,Y\,XDC=82b0X,_bBhm.Yrs00i#`t"pm8"$;DO53J+7Pg.&>]_nDV + "<[R;&8F!WNOh]aP4MfZf<_M8#KsTHE[OSDjI<0\J?,Enr+7V7dU&LqZ4Q=X@'MRGdJ16!r:.&onk5Z!LK,J?Y + 4FJa+\TQnO$:4[7_J:=Gk*>u&h`j8m.LhW,KI8=$j.bNEkq-i_(np)YDC$`O\0Y%_XEsdnn + >r3D#.s?qWpuSFMT?k?RRSE`imP)n'audPfN?^rToF@Cl\McVZ/=Cs/_hC#a$W6#>]BObEB + Uk0GM`m*6cjVN3^mVo%*s0lN:tl/kGRdN<(ZY8C5^[d1Z6YOBi&%AC:o10Q:pXXY2Bq3AE4 + :e51nLOH3Bt,h8)-$*@bG/SPR"AM^H61[qj*:qkSG9ilC4fooDF%AbrLW7o^4gN.-?F)MPj + b7=,gh/N5HYle/cL>K7p&FK3(D>Oq<[5lM?&s^-k:)c6V1u`S^iitdSo=X+i^o5XWjKV]"P + Kr9p@cd46X-;?%'B'a&I2C1[53h)k/sAM>Np6)"l8/XJPR0V&VX)X4lk-\mCFqk'1@7^n*/ + <8hb=N]g2"%DFml!m]n2$ + >jQ7M3BaE6!!KdfPn`0E5nBR;`/]K%98*?22IgpB\Tk0T,!VM;"$?FfamK+3(b2NPDrbn'& + Sfihp\$i;kI:CU8;q/3jm!AQ-273cTA.V47Q$-2;:6U='dUfjj@^ho@i?Uh``pj(QNt^(\' + ^"O$?S=?u_@p&Y";-#3us2@<&=5nZoH"*F^NQm1^ofB%^Yc04Qgo[5L$4Q9@UY@mgh**c^R + XdIM=B;.W^,AW)mddKO4s@(LGjo*_sATA9`g4qEp"g&XH2_ + pQJ2cK;UF3QRa&:Dtd7o]4U?e'q5_XQT,Q\H1e._/bOF4Z$NgcHL;#b/eNGJ(ONDBi`ErX;*O"VaQ%K + h'XuHMEDkllf&`7Q??b:5Y%_54EBY'!Ie2p.ec1,B"+RJEJ+a8#2@f$'',jWXJG'q50#Fo[ + +WIp1*&]=!.m[YTiuJ``A$Og.'L-+b6Y'tTA@,!?oM2nq;Xnc)n]r@IcRD=AB1@,PG'7dnp + 2btO+\.flMbOhGKf7UYQJIMjTh[A@DOB0.f\]I5V,(4UM>$a@f)8DRa)Em@E-2N4FUnPZf.G + Sj/h$0cpE*9l,1*eH;R\.'=WK0k+YYi3dWm9_WBYIs7s$aK0ssk6$"nAq=`mk;1DOCUBodE + pCNau+1j*r,$+G+YIoZ\Qh),Ra\O2'=asLb?\2q?(&N7fu788aZVNFX&\P9V$8#LTmVlUbm<242MVeW:`CANh*HB. + hWD'q6V-]bg;n%U8+M/qL/P4_`-!E1cT;jer9cuH<"0sOQ%0f'\hP91)M,meWA1A>!/:p%'#ZSV2A + G10*k%?\"obr&F(%=)o`]2K1UM[l4^=a>S,1OWF?Jt\Tl&@79T(TBE7+h-th\1SH)EA9MQ!q=VE]q*=Wk5uo"WH3 + kPa-QGn9Jkk97==kPX4/*QNU0'JuQp7>?6F[k%nPiSCqEH*M)hj_NL!MWPmlM\U+(p`_V7: + TCpidr"H`!0Z^NbA(eXJT)%/O96ulC[_o)s\#L:W.!3]^f%$eQaf?9 + '.Fd6)2Wp'alRMgrXE`h,S=NIF'?W<-!]^&YPnT67hbguROP9;g'(:rdrK3VjOnCX;R4$65 + Fj61CZ+T%;gEVq%-^7O]=4]\8!]?A=?B;:>n^?0j]O_cGnBLk> + A'=gV:)cP[p:e9gO5LeJJ,X^>[*3;S!)ep:L.s9\bFXc;-R@*eJ=XX(A7:2F6R&BiOg&i]M + nX=RI@f6WP&n=5dV,S'H)I+iIRPbJ_TF@$U*1m0^`5AU!E>Gp-eBI"b[BsgU:/QS\`-g6en + 1RB"T>ZP]E*0i%#c&TB_Nq1)K3no^3e)^66!o?q[YW^1=+U!2/O_k\/ + *c7&l,NgpMYrOEZ&+`Turb6sMTB:qQSd8q[).81iN"F#40gQ4%6Nh-0?\0)eL"IPh#[4:mJT,1W?>80 + JHK6@oban3R6hIfpiZ#I$MUWX(\<%\SHA+QDPe)o2a*6*(W3"<$*iK.%k^#V^i+K>\rJdNWPGT`J7";8NRO?jit7APM@@"JY,IL%!H#BfN$j:F6Q%7((XL`g.4(P2g + `JQO$nLBIq"!`2.@&0]8L"5n^n+>sK6Jcbf<5ZA:."9_!jrWjVb#R,e\+Fl[M&-W1:eVLF/ + Qd=pg&3o(!8*gi`"F\&++nU@.-mA)3*q22rHQ\*D#^RVKRL"1*80QuE*XEqGJfW4l#^2kr@ + L.+LLJeD"`5j+KJfNttb^ph>H3eO6LDgG<+GU:0E(+gSG3._oZ+_60?-m<"sTS7[ + t=!333#Z`(%?!r7!80Ll_'aPsY>bHd+#Z@=L+p_&=Kb]Dq'IY+iJeR>k4_C@?l3L--K\?]+ + +GA"DJeJD5/qO_W+Y.N)KVA`DTS/I68DQf[+G(3h+WQ(GW$*%)TS-JS73I7U#Xi%@+V')8a + <:nB&;VJ7KV3u2#XG&9!XM$M80Ih^&#^WGJe)N;oo>\tap:*PK=V=MAB5nYJe!SZk(4(6+Q + Ji^K7X@fTS%7j2WmURfRaQG+pNXlkTU^jTniIaJ"d2E-)RDmZ7q:\kh5rr+6`h*OWWg2rf_ + ->6<'6t#PqbVVFUJrJ#:9_E.=ciH?c&1$6fDBTB\01U`\l!rhaVVEhKB]MS]:mTBai'Xrls + !rigCc'$.C2$Xs%BTBgLr\0(%!rjm0oZLGO\MuipmTBm0h_B8,!rkrs';]*P18,FK[ZA8<. + J$TCW1k2j&q`N4^9elXqTC"rDeK=0ermr7ZLS3no + [18]0ARn*u(V+:BH+6m*\ + WKI1Wa82?iC4/pY2,6Bd0`P,trDepO>!LP^s&Ln;=fYP?Y/gCM+6rKJX7?1n8,C+FlP<[*F + cs&]GlLparFh8bVE`Y1s'"I]Cs6J![oW@c+7"`4Y#52/a85$Bl`P!*e_[r?ReIoifRL)erk + ?GLr?(A)Ej-;`_,rc,+7(D*YgNHfa86GjlqVl3<\DBogE.O(G^m"2rlE.W,p2KFK!L\Cb?9 + 0J+7.'uZVg_Ha87k=m-]b;epbtL,=YBupY!'gk1&bhe#K6Ct3<(u`r8kYJcMHF6msZq7q44eiG=VJdIkTd[5N + i+JTFpQ5kInbT(_Hf,;>NM5!XTD0"@(6Us$4q;$UL^O#GD8EKLf3pTn32tTgU8^E4-S#(hX + 9);#`t:r>.hgE=X5b5_r8M+sZ(GO9bVKM,>TH`;$)AiiH-P6iAJFoW8nge6\(GMD@Xha?S- + -s%NUo2+G++)l&(,Nfh^O&+oKYe):7%dI'.fklDp&,_T$IrC7Ck+@"A?Migmu84b6Z,o$F[ + <($U^8Z=KnAGi-cEgBV(62n!AqRDf2W`Mh"AM#48[TN_2AInI%0.#DgQno4]b^#5X#Q!:!l + [i`mae:PgUt2!X\::[_rk+$_oUE1-E)%k(3N"Y7+7'PgY[ROJ-Z&.@'?YVsC$`foq\UFABR + d;/q`cJ4!hbbrK/0/"b6Rns+7-k!d)mM"W219(bkQ[p!X2'76Ukc!.AeraSOk7f-u,kbQ') + bL8_X7FVDQSe[V!cZ$)oj2D8^qQl@Dd$]6C3M,c8'Ps&,k.:9"dE_ppNR=BpCVWtfBP,td)T@L,-kH4iHqe9o!/\&8;f_esp*H.U1_ + 74\)$pff5K91[!KOb#E>mmJ9@859?8Tn*Fm2o.l$n9=A'Q:O8Oounk?``\M7hNA[O8ur_gn,>%^O + qZFUi_f,sg80P2lX2l?:]?]5&-O8R\#Z\ELke%7ZG"t;^Urd)nYq`msArLhgg.%oW#6ti\? + USr;FdOa@K;/uW4[)53R)K(TmRR'KP$W3_e:K%l(:tIR)0Pj_9Z>1(cXP7X#Vle7;[p=5Up + %H2#/deJc8u)B/6]B\u?-+SLat!;i2OViKWj80.[%/DL^ft^t@rNrdfdjVe2`Ssh8`$p[^` + FE2*\2LF96qY#HRrmr/;`[Okh`fEn$i_L+"GSY4+n*%Kcbr%/NN\[=o:7Ho!k`YXdTd<%PRq>i+T%HWC(d\,'=jMB3N//,X0/!a-(Dq1,$_E??+d.N4M"uab:'5d.]-_&(GjH%,;,k:0 + W*S[`LGF+)qQlLFL-:9Z^b>b\p;!44K$@dVqXJV/)_lqi\W]LZs&0jHtPWXM8"=/W67l5j6 + IUEO.'76^=na=P*Eu`;ju07[9N2NOg1mmF>]2g[A-qjC7guiTuW'tS)[kjWhl4(Vjo8IMVD + [M>Q61paf=UJ[ZIu,UME+UZ^i#BSL\T:)fN1o#V2n-X3?G[6/B"KG!O3d*[8KmlZo(3HFLO + !/:$N^SKV*=>\83'88c,W+S2d5B\?m;/WWPQ`"M"@Q3=I)SRc,Bh + j%;t/T^.lRCaUi]'/#fS3MIr#C,4L/BX_p(V3.Ag<"c!/PlS'f3%0;d2QVW63ne(23[>Vg + ,;1Q$?=Z%kK[h*qc8Vpdl.j5?==iP$jS1eNHYC5+o\Rm(oW7[m&Le8#sQla6;$RY5Rmlu0W + 6X?R&Y2,dPr4'2fIoZ(2/a(`(SHhYR=pm]b.W^4G@]DKXlr5[L08*[,pN.=.rJNsp$*7TTk + +je;("NXgOaD'0qHFY&TM*R;_5bZuKZG/>&G=^b=NB@_C";"-:l,b1`8Mh)%g_;_:!fcVQ461gf\PTZW6#G@#"augok9I-:d-*CI<3IId9OUjdKWk + tjW']@2PU+.YPL%JS2nco3m5)nbKC*b[(\ + og^I.Y/,Q,A3g-\Ob6HhPZbXtCL>83m?a$b$j7"S3&P8_(Q_b@tdQ/kY5jA&D>>YP>t9b^G + O"B%"%be3#e4Sa"dACj7jCe]jjc(%q]Wc64p2((fJ7F^0_jPM>Di%r5t:)m]f/7QmcJBgeT + -q*2eAEQF(kU;DS1noq(XD5JNlpenZ7apa"j&*P[NkY^:588,W%!+WiF?`'u^J^W1X'A/I? + &C0.8#,oVX'rc\@g^G/5NI;io4L;rBT$\Y^at,b4)D]2D@?r/5]hfYK5s5EahY!9^q>Vt'T + Ko`Nkjo@5l/I/Z[BkFILY'Ga\3Dj=n5.#(P0hWcI,'5UoPGYpE[Q9g + .6Zfh*Kf6Nt3]cdi9>iqceN5iL5.aBP9FO21*C%/p6T^u79N4GSAOJW@8!QJ5ba_8KW`.-S + 9?bURV0l"C'?<4od(H&c73'ap/CUIieAY,3`EHroCG&U[e&@O$(W>&s[+nZFfgXZLQt5*nr + 8?,[hSpeu)$K"i7-.Goj@3qHRAB&dN9So/l*SuS[-R(d"`$U!;V'(]f%ff'>k6JLo:%l7WR + LQ:CL"W.F\e9G:SV=4ZXQGTGu*7B:[;UWqe*tjI8D5=:bun&6Z#A*JP^38:j[1IMfPlKKe) + Ku=EA]%jUPC`%p5lZfY%mLam@[6bJ@2>\1AFnD3@bEP;mED;5KO"h3KXNM`AqQdI!@E-(:t + >,<7O>g!\tKD4`hA.+3O;dZ(!3`M:khUnQV@Y9YdTH_WqK0Nt;->-k,1`A?5r0&#=b3kMLe + UTUgqZ7odn4"S+X6OVXY%6J@89Ja5a/\3Z(]K=oh]Cij:D8&tr_GlN1e='FfX!#@J9Y?Tdg + g*]\D,<@F;-]Aa-@JfJgX3\kff_ta>jF5[F4ealg,#SRZb,#+jPi?"6!\7m^*D[`CZRC,g9 + d@4^6IqaJr\EHm8g6L^>i*?*;*A`>?,/M&@7S4EWg.k0X(= + 2*J@:'6=5'pfX)(=*D6%f1X,CpU*/V-^K[?iTd(=Htc%o3LjA>-\5n+C7e.UEa'3O?13#*0 + 2%2L$c(G-mMd?BAP]p7!YQI3m760jNXm2nq8+?:1+8jefqbp6HJ,rofh=p68s<@BK4.oN.5 + _UYJ\c)+CnU!i>,deY:h$M_M-1:iE&>2;&>m5@#L3BH65>9,`Y/NUKh4hEhkgKd!ID*L+06 + 9CFM>Fe+8X[B_M7_@t'gY3W7V8'Z*9N7k*<40V+IDf+@`?k2s13?H"K?hG9;V\BQgq2"RK@ + EVlc;f;=NF(Xg/_5.Vll8Jq^[6AZNURq->!OrpA]=9^7U-qX\-<0/%L$91&I<9rtPT*D;BqiV&,'DBQjAZf3TsU[BBq' + t<17OeckB`*L"7SsAbf?cH[)"+3B + RII.5Pi!k`,*#:LldpP@V;2^2qX,-.TE+"?kVV,@cg0i0q+O1HhC?Aq&-LLL=kJ\m\ + bUbd:ib.s#>'$E*$`!,j)/)/g3d`eGQKW""*P063ds`CV[d2[+N)RHM;q?s_cL5HN5\Us!k + p$l1-BqJ)Pt[4(9VWP@I7\@#RH"0=4G6MCT))qLSm6We`C%sietHSpdURu2j-3_d%F=C5Y/m7]P#$X4@3NZmQ)nm:,AZA8;QEE2KqlG-e.0[+:hVW>;qfp#,?-\8_B:CSjj5neHf(Sm\V^,LFHS?aaM + 9B$OMf-t[o-nt+6+:Fk?UCi\+V=[+QOp'5XKf4Q*Ek:Ug`XW[40,%/osF)@LiIbi]VhTDGI + d[&h_[4:ddY&5hgmQWnhLHis7jjP:5oHc;81q.@^Y@rVKBU(R@UBU)+8D?QqgK*`UmNfDf- + .EDO`(':O[nkbt7H1?cod*.IAshTH<:>OYoklMKf2ut!;"^]Y@qT$7k+^C,UXa7XZUY%>"j + kf%dI9&nA)lBToj6\^@rQ;:p3#$=4k]=jZ"-esA7]EC^.<4hDK+D"h@c,u(#sKaE9-;SpI8 + !R<;9dWUN^nuGE$k6(%#RRA`\UGAX!5^9n)WgJ'_2Gluhu4j)F,Q$"a4mnhl/B7a'G5bfKM + &d/M>]I1^LYPO/g>m2mi]#:rH$e'Wp"o'I=cr1*.kQ['=5B//Lh5Ei5KgK5`tO]F9rlf7Va,;\.U7oM1Hj + /YTu=;%0Al3OH:_0!X++++CI_G&c\$r5W6@&B9808.`Xh$kRF^0;(Z&">6?6i + WLjN-kNb:$RcRjJI:o["=kT]WWXdo6CJ$+7ApclJI2YoSq.3qZ32MI6=L'DTL)V,JI*_9OF + []$&<)/GZ;LfL+@6tL+Ws)[VL]!Y&:T08a:NZK#EnW\*M>^H"<=(#`6NoA!j@$r?u_>]prl-]L%+6i=pTPc4lLncC+@K**ql>cg%+6o!fWc0G + nYcTFrqRV0X#PuAgXG?YrJ#s]Re@$sPOoDk_l_4NX,D>+6_p/U;RO7f_Af$OG$KDf^^Ktt3b,3bN=STV*_rk$5InK7)BCT + gb+^/sOu?\A<,O7iW>fGOWrU]#hjL(_AUCCqZT5_$$nrl%D)(E`!SJ$HQTa&r3^A>B*d&,& + a_fgu-SA,V^Sp0U.SM_etHjF=Lerm&R]4Wj%gJWP&25KHCq+_1G)[$Hg&)KS_i9h:8)niB" + Vq#;TkAZRc/]\"/:@O:(4R#gfM5KTs?D%3bZ2[8?uMAZ@AU\pmJnrhj6_gHCJ!Pp`>Y!:(M + s!qt1_mm/oj4oQVEb2>%QI5\9grl(+Nd8u4-V3J0q#<#mr5#tB;X7V8Z$($8g6j\pAOlQ_G + Ac[&49pQ0*[.@%k+$piOoB+P>KV6;60UO&W,>-$]-$2-J"-2@p7Cm6Hr#mq*7=b,a8++)k= + tW`Vqh1lPOsp?u-@*0MVtk<"_FB=T:[kD@J + nq5mX/0-A9H^J"cE3LgagEL%*)"_cUgdd>7flA,R=`fQe#P'L[c))'P(5MEfNKs%+q'-:J3 + :'dC"c9&ok$O7]kFdd17tOb,;W8Bi$9=T;*jrATf0iu;K=E/mfD4%Ei\5L]u=:iZ%ha8#f[ + j=H(Jl:c],;?!EQClFMi8kMZh0d[Vps&CdHV,9blHr(So_+le$]jJcmk73rL+8jD+&&k'Va)cTX6V47qhiEBh^ + (?&Q1C6a8IH`FLIODVR9*S8M+agrgVB*G>DFAC8_:-TXQZU;//6cWGUn5<8sWP>T!L@F]PQ + QQP3DX).bBP:k.%'`E=20R]PW+]U.5B5O30)XtHj,f0^<`9T->$p$07mkBQGH$(j%m'S"bN + &RqhOb^#L:fpd2c5O>54ZPI9C4j,m+cn+Yjs)p$FgkDu@n"Q%IMrDJO]XOL)_ruMq&a + DZW_72]8iAfX4Yd/OMZ.sif]K:o/m!Z + fE/Ol]W&3o\S.`IQ37q2-TW)YC:+8FG,j-`+Zn`$lUWod:lr.U7g1pE8`-"F$ARkDb\:Wb[ + m@*BUl[atB!+8Kh-KNu9Z(K0VDF&t-AN7"*#AD`mP]n%k,&NY^HFjBgFGY\bC`L+O_T4@me + BMAq$BF]l19X0s]_>g;&Ng$2:91q)'JO[$$7\gJ<:_>iZW@:27V2\fKIN;PBolW0/[-=pGn + ("PpI^uD:etb6NRJcalJ(U#X_UW90ZHEa&EXeV@k:E]I0\XD/gBU("9%\7Ni,pWg0eLCg:! + 0XeE3H0]h1IcEc/>h(Nj0spZ*6FL#GU*K-@4u1HRh#Y%L.;5$FD2+[&;?[0Oj"]io1>PId: + Tq.rPYf14Uu8\#9eH-kbT.roAk\jNEd?$_2iE>!d+5KPk81kYf$+^sLs(8B0tS(l^'hNGL$S%a'fLcGN3D_oVQ=`=hSF + #jtA]O\Afcg?1ZS/[kaQgiWtc+jIrK%ER>O-Eh'&YOX=%Vh3a + o2R)A+Z"KND(UWNlXu`bj>u<_hds''*@Rc"R[o*&Bn%7Cg/VhoT + F+en'N2raZ.=>-Le!*nF%#(%#1%KO#)h>QEMS)O[@1sI*.XFo4a0TYu8c%Rh^dQo[."<&Rq + 5'd[Q3M2U>d$O(@sHG0\4`cVo;eC$PGrIi+.LT?Rm,%ud1-OSMbBdL%&DPG7n'0)BRfBX+` + :nZr$=RephpC6?7-n\Y2Y`bVj%VP_HU&()CneK'D!*Qj+SOlH?pk5KAcDmA`1-OCkjocK7i + EKC$uXe+6E#hl$9!7DG[&eoH9)$,m@!QSII$LZ]Od3?id1N5&n:c*FF3s2cQ"7ed(nj]oTX + X)7f,nNp"8&$@.5^_!e"nH.-11kt*C:2+L-P18F:j".@s!Q!7JC9&U15?-K*=/^-jN^[QDH + K4ZRjmj!.O\.DE2P1iWeM(f]M<_7o"+$QEsqQV$n.5hONB'#bq03r/PGqu'&*l$h[T/9N&' + P3B[JHZm4T%@01b+VN&$G,rt"]'&1IVkCeF+jlS1$];fNU.Yo>F^2)6m213"Y*;(BWT&Fjg + "n\p7sZTf,p6C3m+'QZn]WJM>UZC,c8'm!P`d9N,sXIBf52N_]3d;5=ME6_6kp="E`-7'KB + Kc!aET9G6OdubLT#oQGSq(J-s&"[E2V)ja;do3EuYHT3([2Y1;47,f@%9+UXaS_1F4Uks#/ + SZXfgAS;64tV*[9n4[tm/GE&5>@7>@_VlIrr;NjEj>oMEXMlF3rnQqgJ,:'XhZX[+!NB`,% + kgo%/O!aGH8Fn.2'[ono'h\*O7l@\S():%3DcmjPr>,!G( + Gg6*F*e_0Ii%A4(bG[\c\"4GLh+ns3H:BGWZ*tgT/n^$U9UTE.Q#V:Yg`9C4-cj.bt@lX;& + iTF4LMd`m9okZB,u-84k7_>%>3?d6&f'H: + FM=^[-S!`flg0J,Wb)acM)hCpqGP!NQA6XnP7hiLIfnl'%_fOUH%Zm1D2dDUfD=%fLs?T='X-mRLGG^^1MG2N:9cOU?VDYl\NEk$hdaj;*>`+ + 3d/U8C3I]9a-WXXd:nb1X9jZiG&5V8ZpFn7kr2aHZW!AkSHcl6A7+^hZ&gsdc]<%N7#0X,E + NhGQJmWuYZM,2Z,l,jkZ=Q9)A?$K2V*iG2G(X'4Km-RUMt[h`/VDDpP'K]G%(8="$^c;."I + F\,+c8W0PT'VOTmWG%NsB[j3J['OIg[YOLd^!;T$1o4lJDl7T%fcE=tV]YR@QlgjK19 + Ijr'0^S5X0FliSPlYaP"R^C=DKQAMoao+t*Dqk"&m"m-2ZHZn'2l:G;X%[i/=.%c3qDf4ER + dKu'hIrd<:j0f=Diul)\#iSU,[U[qC)nt_kS'/N + VYREQIDW(T;5SaD@YnBmdA'J3/ + u?Ps%'o4dV\;3qIYrja*-VHHMk`pU*50\KA;o+:>f01^UEGbEp]mgQ33*q]D2lh-fWr!fba + 9\*9<_iL0="RP7%6Q7ma[97SirJ87JNO + M^n$M3RgmdfkUB$H1Sp:qOc*qH$O3rt5hG]fKY^(BX,(1@`!6N7rDQO"1o?0tt5MNUK#j2, + B6ZpPul_Nu'^E)##s_kE"/5_"e/7'n#cgMYMQSG^o>46b4:tJiM5'^90Rf-OIAR6&IQAW/koEDJna-W5cEs@lnu]D;O18HjBjCTeqi+8DBhUL'9UKe6H^iD@[7ust\i>>:Y + .LG#l(]s(],&fBp=2sm6H":4:j^*;\l7*?bATO:V>.aS)qRuY=cAehebeBH?"^(fgXb-JW- + (h5\rD`8=kD22O#(_,=$P?-$$9D]3#iN3]G9+BJ_@An_:N,4*Mmq-WB+95=k@f6'r@*62m' + d.>8+t[>2sl'6Ikm%4]tDUHLY7q='ZL(?!@,Oq-M^a@m?AnEEm?e]Q(1+nZ`]9YuDCAS:4[nlk"^N+2J3/60o/.."VreK;]pT#WS3f#4Opc&8MCe5mDDO+NasEJd`cRqC + E1`";\fWO?m/5#V(E5@"O1WSd6[E_$$u.,S3HK&Qk]X(a+D9++a`jJ_OuP3+gSF["9',+_60?-m<"RTS7[t=!3 + 33#Z`(%>hjoG80Ll_'aPsY4.q9_#Z@=L+p_&=KbX`B'IY+iJeR>k4[l#sl3L--K\?]++G@u + jJeJD5/qO_W+Y-?]KVA`DTS/I68C[th+G(3h+WQ(GW$*$]TS-JS73I7U#XhaM+V')8a<:nB + &;VJ7A"\Jf#XG&9!XM$E#U'%s&#^WGJe)N;okg@Sap:*PK=V=M>_jNfJe!SZk(4(6+QI[=K + 7X@fTS%7j2W"c_fRaQG+OkuP3!opJ+G2]\1E_<"#VohE+NB!A80Gd$$lGuM0;*pf#VMd%iW + q-s#U$L+$Rd.%JdV"qCAJ90$!LN!VJ#a9J#9.?DL\Q3qK@@d]8D'1W+n?lUEAb + mrhZ7PC&Y%"$At^\Wp8::XWQimri_s[$3%4#$S'H/X_W4][iapmrjeZeWW'O%$d/1WYO!/+ + _&r"mrkkAp8cH^&$u6p*Z>@)Nb9-)mrlq)%l2K$(%1>YR[3tiJJ$iqd>GnmrT:X[nXh"GdT + C(>2hYRcZK&O6&qi$$8-i93Z\Yo=rJ%@j;VWtb\g&C@\mcj`>TC3*cnXo8'o^faL=mmHq\: + :+_rd;c^ocNAPJ"N)DIa@9Y5\'`n&+g2ncoD%k,Q.e,oG\#HH4`JW>^rGUre)^"9cJ"c + XIX8a/ouV'5i + '8,?:/l"#:?\@.`%qI*'mrA"oUc9KcKs%R#%2NRLf)\\*M:9!<(Vh+i=a813+l26%0&cSH` + l@i(",t>q + <%#Y7^cH#Q"$6ldUEfEW3[alMFqhrIL%'"@(Nes'XmcKTGgB5M0f?@I-F>Z#Tc^L\hr2lth + 0VdJh2,q]VTuNKUSj<^586s(.u(Q]LiL5M;G(B!ajnZ^,d\a88%C6kFt]nds2P,=lNC0rHl + 5TLK1^)B\$[X#3q`f%TFsCb:=+&+Wsiab+5Bc)J-0k2>$aO[(#M#_/(.nD2jRI;eNW^m-)B + <#+6?qL3-Q.u>^MTR-('1JJc8i.q.t*^?-Tq353Q8YH6;89o:CN>H:HiK)s=Rmc2dl(6i>q,pUp9n,N + aCbWV%8trcO"lfR=@Eq7g#!ra4FW4TkWT&+eHS?j$U_clrWYa;BC3q>MK"!XV.0.g=kT)hY + A;#S6PQ5L?2NKM\*O6E"#p&J4t1KDcGooMH%UPW":.l5i=2Cd6=_s$oHT*^huA&Y_ZcM30M + =&+l5RdW#4Y&PacDBX&>IjBP?qr@ecS`>G?YAW6/V29+;&S;6r_+6hK>a!gYD&A0;\6h+7!$YXb$J[VC]ZR'.RTfl(#_b6Ki7sU,5$GC=f@DX9hHj5=).B7OqM5'fSBD]VR50d2MuTO$.1qSA,`-#.Yuqcbss4 + :6,=*.R8^,>;K4<9Wg3_-jG3\;X-J"83L;O(bFs0&6iOGnWnCUI9<#'@+N?Jds';\EHEW@j + gt2-WBH@`bYa-3G4GT&.c#o(mp-n7L4"Br?]pZ3FJach&Que:OlQ>h`doqOdr,#Tp4@bh=& + ,TjQmApA>]03nO>"IP$1Lfoh;B]q#A@$gDj_&9e!@4\l[=mJ)i/;Og7B + `QJ!j&/Lc-5nqL&bR9F)oCpLR;1s[9>HSB%_aYfs(B3rdHDiahLS$Re0S@Qmt0W#jQ#QB"p + H1lS\D#hTno-0hPs$LIZ'g_%INFiU@Ro(@/V"CgVUD.341cdemFSe_\3f]B23`=LN8Ku]%H + WJTBa\A6_ZmOM[\*Jp#h.dmQfB"MjloLHk.XQoMJ*1CQm@.-]U9:#lYeUof*:mts3RTE\dj + oeL[gCYF)S2K6]Uc@B^nZ&no62`'i[d+M2lP:d]6k'\gFSKQ/>I\9ilrP9JW: + 0mpUG223.^ueP1UlIg)pKVO->Qr99-.>#HeC>\KM&81(?61jWO]g+VU3(e!R\7lQLTA+c>_ + TZ^mf5T!'=[Y<,'s,1MZ#n0W1qJ7YQ?J^+u$UU1cjVR3qEQNK*\69ZBO=T + UYE/[dQk&-6+_\=4-E&pUD'Ge'r7VK!fS=L'DZZ:n$+P$P_q- + `.8#VAh&)1X;k%ca)hLZO1EQ/6BMOD7rk*V[GG])q^N6fr&?6ZbguDid*cHNBQi]^&p^#)a + pVTj-auLW,q(-P);*%Ma!;`5,1l/@#$T?VR7*)WB$t->]7#IXu0]j59cO8.\'!r>+9;3[EJ + 9D$+hZqeBu6m,_mWCF2IgAanu\F^YFN4>Mj%qbbQFn;d]7*2[fF<)'66?i]:Gt + 8S\'$'drr3J%'1o;XA"Y`Z0&k*&>#\+U[F*F-n+DR6m)'+bK,*PFW;,SiQEpY;.4R; + B\(M+Z_]\_]nq$K)?gVW"QG93i"[<&hl!r\@UBN3pWueZNm7`$-Uf(,KmGU<'RVH5qrMY]8 + B$=S%?O*'Vq36b[\@4gV6./)Pn)eV'Ekm_o+Z:9X5R>1-/T$M.Sl>b/G#BV@tGV,L.)-cTM + D2ZMJ8k)JVDqH9FpO`M5>h'0+7SfdB_P4g\P%dWr1ki4\E(^&-a-1IJ)c7F"im-tD@&A7r9 + DMR:=d^7('#,>UeHlpfIk)O,-Rlu0W6XZm/[2/?;a4'@E8oZ(2/a(`+THhYR=pm^=>W_pUQ + ]DKXlr5[L18*[/qS:Ej-JNsp$*9;`&1""'9"NXgOaD'3rHFb,eM*R;_5bs;YjGYW%iQ0d`a + QsD.>S"p`Xhb[!_'=E#glt)mPsJ:;_.(d$4,$7iL5t=58[KF._>_R+*D,<5+=nTdgQ&fIU, + a(m68YK)7&;%(VF"gk6?3aVK'K%OW^J1ac7a-6MV$+n + .PfcCib9]j+1'1QOgeH#Z:<`Q>bmPmG"O]^admZ+C-;;rFib2&&^Rl)PrgI8L) + 7Da.8dlT^ngeIt-R)EE.,[sM(JPJUD:/kdQ<+[t>jh>j$5)Gq%V!C4Gll&Q6W>e\l/8mC2O + ?Wkf588,W*-4>$F?`(!^J^Z2_d1<8?OB0r8#,oVZXZ5;A;]Gs5NI;iqe3bQC(#]H^at,b6Z + ,.fDi>rs5]hfYMfZ\'FUZ3H^q>WQds44:eNibg;%4+/)h(@9'=\/ZN^OW(C_k-fSmb%m=T`$`]d.+4$C6E?W=A?6T_3/Up\*F[aS`e_gsU2Pe'5X]-D^96c?Z*dQ>A,$p.+j9G=n"F@&YX + 6VKg;bZ%#)A4@ig7kp8u]G*rM.9fKY9?bXR4D[bTJRqOY:e_[#]Ym^\fl'SY<6\]J4WINd0 + mQKX=\Y_p]l[JG,[AUL?-VYZc2:qo$t-!W@C.!34riXt0pY:6k/M6#W1<(kN9S?MTiQh^)E + dAc'Q).];a0kk:DIbC,?IYuE6ghWcVg?!AG7tApfC_rf51;l,PsA]I92s<=02E[HiLO=J_/ + ]ZfBi[Ke-%\rL0,H$==je;/-r^QMV)2BfPM&+KFKl/`E8SKfWC&t*0RCuJ&t?7=R?l+r>.F + ^(g/.&+-d`rC`Cd>Ma?0c;=0`Jqi=VaRaN)];C^_D9dI8[-JA>F=q)l?Ua`jeX4f]eg.a-/ + r%:#EYZcH/>)b6t<&2%$[+`2Mg`2X"5M_4&r^frO4%0\1o6rg + S;DL>t!Gl7GHS47#Sn*Xh[o,_,RJS`7)b#ou'hk`DlHP72sIq4igVTa]1FL`FI=jL!3P>bu + KDI7B>%HQ-_=g?Eogk@ZW\6'rc[i9B + ZRrk_(L+E^V2iNbUV28%IMGXb08L+r$ecDl>O"CJN=VHM*Po87<)VIc?fj,V$C'!"6h*M:5 + \I9Z[-(IjjSN0E9;I,n,gBBUKrAr[b4/8o?Ye32!laF_H\'u/&e0DQS27lgS9@QlW]CWh^S&-oA^p3dT]O];MHR + 2oi-G6+sbq%$o`Q5@>cb1%E4Y&l@l4%9u'(?!T/0H + Ojj!<8em:U!J*$q-*EHUaI=pZFq&S$t5fJN@Fo>p3r`H=<=N1?W4+QJ="(pAN\4d`!@b"k. + S#q,Ffj`_!9V=!"G#At4@-j\l:(URnki]m`=rr-$;B"`r/QuJ3iK>DK1bm^UKjiCZk\N.\h + MC/,MIIm:3s7XW)[^9>^G*rogM7A[Pf'aa.b$NrBjA_`k*P*(;sBN?>UGqM3a:jm:u#ZSj* + G5#"BA^(U1:cMl9&5)k,%,;Vdp7a]t@]8dm*-)ij:&6h/c%rI-3>H8QK=pep^9+[S5!'9sl + 562*OfB/l5U?=2-7gCT^TBFS<,kpGMBPhPOA_*a$b.=hiP(=1.MD"n9kZnZ+"Th`V,"9a4s + .\SQ]gd$=CQXnf%&b29lAhoq-gg;cbpc;?0PXL?I-iUH!ZQRrtjd>l^h%L:CnMF#FDm@1(g%Ogu3n5M&;@_S+M*qNk&7q2-^ir^BAB3&7h9\_i,@nrRWY+ + AW'T1(s@gn.kWX]=/jraX6L4^OsbSu*ko@bhi1n1E)X4Q16R?uTIcnQ^?Tj`bp&GYcS0C^ofDZY*jg!9M] + 2lpZDDm=8#=JS]2sAg;>.2$rD5$LFUEEa=JtmV".a-p\)pk-lhf7aO_6/D17[m4FIf4IU+] + )f8q:;9q!hmap-Pf^hh+F'PJWXX!5J3)Wg<8q,UM(ReMR.U"ag@:k$Mn:35Y&,%7XLBCsMf + J'6C!X?DjYFo[`S.0`G?,5&g29ZML*eCHOCM4X;#.0T0eo3L2%:]KB9H\*F)S--TJZBo.\_ + IWo"'!(d&2cT`#,ZsN?tr4NNWu5U^sk%q":[Qt:KATPfG\c7Rt7H0Bq$(O0`t(ada0hQZ3H + PEUu5r8)Fj#p83kSl1is0G"%HZ@BXes1#)Ro*0ebFH%1ND+^sJ0"*`<2oJY6Ca#TKpB*Qn+D7&ZiWaUXS[L4.X:4=q3P`p:1QM#1E.OM:B':,AMZYk&c#>I<%+ELQRNQ3e + )4Cb@JXJSR1E6hB"#.O7*[hnT#$ukI?tICsDqb@G5g(i:`!ijCJX)m>=;b!@.A1.7!eH4W( + ncG:kSTio+C]'i-6Qc$"slTL(m9H,#T/SQLr^Ek%j5=*"sKXK&dBJskSSi$LZfS&JVU0@Go + 7`5g'/Q?@EJco7q``WJVM5_C4V+q(hY'9@?Lg3TOEEV(WkkX>_.U-(g)?CEl&"q+CRkH'Hg + gF"qs[D(eT@4L_rU4KMu=a&>3G5"qRA7ncfTLkSQ:1K4l)YJV,c\"q2V^\crNb@&aD<;.pd + nJV$E0,(Y=[U':jq?ucGUTO;45"k2ZP'S1f`(_D7Kog/ib+CHZ'![(kk@Y'&#(]t9SVu:aD + f9P8ZJ"Oe"qLP8p)>@V`B@7!3TP)+GHeIU#m"(b,)QKukop%i+6c8+PpM9-A:LI(qIkAUa80b)VapT/[jT< + "KVKXS.K6+bl;@PX+6mkFW,LsaW2hF=qQbUOL\eZ5X9\U?J#o/lbd8rpEW3D=l[f&G+7#X" + ]6!;ToY*!&&cTS6i?oooDCi"!<5;\8HL#F&;(5a$QnRlJ"ZoBKSTC6]=@#YEf1EYi5J"qSoNedHnLa`'A,UA2/#Ppq4kpV% + W_>]XRqG<%u8Lc#sZp3![s%A"C0+[]j5LU2D9Tb/f-A9m9j)`bXK+Pf* + ^U]l]?V!EW3O>qWf6h["#7SWk;E%oj8sBHk5oD5M,-!?fV*8O7if=Qn&DM/2Vj\_>_?mq[j + q:9RjB*eO7lLESp1Q\D\Gp1drLK#CHUX@u"!FB + :K9,R9d97bcBh^=[1u^,PaN4B+A,M@p!L,UA8H+R['mEsgY7!<+626`UF&$`$3X7oV8,7KRjn+%hp + J0tD;lk,e_:TUPraX"EOWW^[gUh*?miD#8GQrhNRPFeU8,9'-mht4La.2cCJ[HT^gjbfUrb + gT)epC24mlgulE_#rK+6PNQ5$-2/heV\00?!:>o=dH8$ho+rl2Ep2!o`s;J_T@UJ`9[79Xs9V"COLdakMMWIZ?A/UW&s>&R/?.qPW3gmjSZi'6K + A7&\A`]2XrZ;bDt!+8%greMduslEO\I-'cR_qRK\.".4-EN4G:!69jd)C9c@$,0$)CB(SUlP-UBWbWU`dB$M:7HmAX;=th`[kuV + d/W(+oc=pIBe84in>H;epH6*i7&[Ua@-HQ1&l@`Xk10=WY + JgX8r@&Es1C:No%nV6%^/bj8$NJPhYZ>\n#T-<=5bL&(oBOdmJjsVAh;k.^6hhMA0:Rs#%6 + ZF)R,*?:9!A:O8LYcE85f$G^2ZTPoCm%rZoG7U..\tt_GI9l^3$1H&p9ZZe:1joE4F + 5l]+icT9A9h4>II'Oqg;J*62"TVq_fd&`Y#:kAY@Zt>?H'L%<:2K[9D_#Mjm$BI#<3^`Q=T + qoq&Pr1U.p[q0`eTB3CdtYoEM6=4&RbFFD.Z3%>kt0LopL)M6#?73Ga=FkOa)&o,Ef.SOc) + **FWnQa`FDBBKWo!ho^3-QpY,>m8-Yfih.,@C>h2;J^CCYTi(B^f)H*-M?-`42IWj=BQi!6 + jO90R`=7XV?O3c5^]c5O*GkdC`_ie,P9a8Y*,Ee(3:JF5TcDF/pb7_$Mj\O\F96ltHM>e3H + u]#[XY:o*6p?KV^3L9&CMld6)XJqrcK]uZ)F8<1F2aQ\MPl-&jZX8n.GW_9LtN"3/0.*TN+ + d-Lnplq$.&N%C"U1lj9?:p/N6:>Wp'&B*fIm_psM#q#+7@$<-TPA^M&Y6OKtGa/\fnR[$fM + -e9IORai^i_b-LN@5XtJ`gHFIPr\+5(1#TVo#8I3M$K'SgR/lmVm>7&KOKZgTR%VeCLDW`^V] + LZkOaDg>GDWjiP$k)!LbNOcmQ:Yt_l1!-9aK30Tf]pPjlOL<49GWTR+"j_5NUFgQYkGs?nA + CFem>1i1cJ]=n8s(Z'0^e@l0(SIN)TG?*`mZ("8OW'1.`]Y0'+%e2Yr;.B3gS!UQOKKC0.r + V&1@oK\b6a?TP>q0GU@aL@((A?T9[bn<>+AApp+^U58_229GkVO,pJ6&O9`CZD#<8oATT>! + K9bsF>*hfd+B+LU10rAV63,!%m3pf'L0=5I+\GNPDqshI;0uTr$_&TZ'*:M6k&(INECtCDu + f7K%^;TVgrt4i%7hku\ZIt50+>'B@n-L5A%!5AZ!AYAjEYrq>*3P'.+tt;eE%*!/G + 9Z>UGZ$C$E]F6-4F(s4Z3R3OE(M=Z9dJo2,qD**;HX1/>m9EA"tN[3Xs#.;-9SMR-W#"7L^ + >HUIjbk[JWPVrN^i*XOQ@`,$$)F?W(AH-Tdham.VN$9X1j=OYmW=p$Z`b4ZFmlLM(C@"/9& + ]^W1,@(p55$`%o.X6C1mSGYQB=Cgk_>2t;GY.J5Z?M)_c!TM^fu$55F>R5qOC3U.nO/M*YQW7+PD+gJ8IN + Bl!5]%"l0*NF3<&%c6dJY*Db9-L>s<#h]<E/?!A.k'nL.57!=YaE!YQKBlqk(H)OOK0k + \W998LkN$D/Ye;RZ]'/Y'ksU^dd*F9Xbj&$W95r]=nDPuJb:[bu0!O]%YjfMiaR&Kom6q6k + '*AeE#*AEa&CV.%#p0aQ>jkGb1%,*BE@!A[.8gZfnP_/?YrP_r4S!JY>#@dHO\7OQO#7MJ2 + ,RDBnQO-i9(`j.ok!e3ZY^KLE1Je-(NXje"I[9V- + /d63mBZg#*Sj5908,(qoqk]26`rP8s]I_m+AN5?1[es1]# + e2s'F1VF#4*&XfU84'&/<&?n1t).G*;hX0t]NV]t^1:++di3HP + F(-b7]6Ui[K7WqKqVt3CDD$Q;Kj,eh('S0jqK]JASB(V%7FHF'D>36Hjk%hG>9>'EtG:P:n + ]f)+gNeZcG`9Af@C.n3`cD)t/WG<4"CK0$R<8VK`if/'B_Jo0Cma2Qrdnc:]:X7oq(?&(]) + c2Jt&X@E4&,fgoT`A$q?Si'rD6X*N[ion + q6'_[?c,Tl;bp&Gc6FIUqmG.k=j!1uU6e3PO%J'^>q'=NG7.rK,/dV]A%^aeP[4l6(N["c# + D.PPDASs;8[!QBJ'kStO#e"L/n<6E:F\+K9jm\l(;Pe%`UM+bHk>8DGZF=O)[:s`mkchqg' + $4lFa(f_=l4DJ1C^4`,%1Y;-l`f\M=b&L-qY<YT29c$$' + &V&kM`HF;J,\RQ_][?d$WXB3:^ebOU'I!%Bq!VQhJR>c>KhZFW3IX.H/2sg`+.=YL%HT1g* + <]P[j]muNeA8WInXsCH#)(03>JbE?Y);Q^!Mk2b_\[XRI[X3Z&p4[`[$J(,gg'3=W;[`;gK + t8%VG[V$sX+m&jLAEE9NjfL9]YYM80.\W0pid)T` + n!D&TgRB_p=BGqD+g0g$=1VE3h&e'4*hd?IMZW#85kqY?C8V`q5;rp>]L=0n2mk1S)]g,fn + 5(W>&%a[$`f!Lk0=H&"^(MaD]LYT#0^F>K>l3LT1RZ^I^Z=fXs$Y-XTdRrh`$ZGMIn@EC5>& + V+.F1rQX_k\drPnY + MHlR:6=a*.,HU1e`Y$3'jFpgW;&$hUcILY%\<&K,d4gT;Zau1bfo?d,m&bjpCpG%\7rfk0$q.9gjb=n,(Ooh"uS]=WX?2N:?&OgphfKhdJYP + eVq!bIMeqd.XS93,HCD),8kbUH!\M40`V8.u9W_di5trrAg\kS(%8VDV[g?H=PIZeVb.eMc + dHd'ee+f25dcLelN[=S*t@Oo/1mhEH + 7LOS+Zs/LeQ-X`jL`G<@V7>1pf;IlmCWXr00CNELMTcZo`>`7;cE)Y74&pDuBm5+;MjoC'[ + RorrT#Ye4gZ]Q#'@'0s8*?aLJRq]2%?dbIpU69!X#].7<;Q!q$Y@J_pN([g/so0dfs"T(Pr + I'I1g>C^KdbHtig=il5B`O49"bh+/2][O!p#SS/sjT=@AR3(llr:CEYL+ + :n"X!X/]G_$87PX:T(K)8i,"-m6M80S0\ln3M.->6G^-"`akkWX(&*$6XqF+IZ@7'#=^45o + ^G)1kH!@Jd":;mG:dBsN+^BU$i.1#62@' + _C!poDA<"K]G"@3%^#Tt#k"bHatJ31sd"U"hA#c(Q$%gds,L`c*L,PDE@Jj/_o8(8.B"FNK + Vq1p9L#k`d4+HJK,b=;\[_&a.XHkG29JidA#a7dL_te:KU:NMn(6#hX_dqs[(^&ZZ%L5oP3.,>&6hJi"U/s,7P\"D5IfO@EIeQ + 2H4O-m=\()!E-&!hb8t#\"p2)@0aoL(#2V(^M:6JeuKOHOQR$iWrh_L!g63TS:B#WeW2U'NcPK9?L"fLR.CJdpodgk$!D=pR#/K3AO<+G3CeJdhu.c@QJG+Nf9EQ + MLkATS!j_0VF%%d"2Yh+M<:6W$&U%$[8$F/KfYf#V7*M^%d0da<7L7$A]h%JdPU@#Ul?tJd + <`sJp&8g$)eu6JdH*5Q&MAf8dH[bJiX/^0Gu&5+s;IU#U(=B%Tn;g#U"SJ#KS$6Jd7^T#T] + RhdKghcL`-VMT]c.aJ"^NO)LnIW=sAftW6,$t+6_DWNh>P2rfHG;)G63i.;AdI+6e(MR%NW + 2rgN4G\oO@>WX8Zt+6jaCU7^^2rhT!T>+2@h.]NEI+6pE9XIne2riYc`qSKM=X%E;t+7!)/ + [\)l2rj_PmRd.MgBDV7MYG_6rJ$:I_;Sk-s;?"QWluE$GG"e)Jb;2Zc14QWcq`KA8#Q$o=[ + %841e29s?rmm_/Jc==Em@jO6+77+Ih)C>bKR^KZqhTa3a8:sJ\OZObo"!%uV!B,jWW)6+ma + ;%%+7A[un:&[saR_PbqpKtc_>\e:q=#Khao\2&m!]JmrZC=qJ"QiAJ;Llmie9HWLMJ#)'&PR%%_+6cUOV.d#t'%1#Y"8sm6oY`uC+TG-QrA5&WeJ]*1DGCMb + J#>%#SVR&d+6i!=Vo\:uPQHG5MBIDIEW2\fqMZN]3.In6%]#>`Q;\-0J#T^PVhb,A+6nZ3W + _"hBPsVKNMSO_Anc#VFqQq@0fRKrY:9KlA^/taSJ#kC(Z%r1s+6t>)XN>@dQ@dOg_u4>2or + p[3d/R=&rGN5>_(lf>jBTm,J$+q5]*J3#+7$^lY9EnY-Hc$h$gE-Op%go*!<6ForIPRR$pW + B4l=!@@LG9tO`(0\=+7**ZZ%;noVop&ib^km3:l7OV3<*P7rKWH;>='3S0kP7PS<*A_5M=] + hB;>EtZmLCP)Daou9eiLW+oXn8q%d^>7B3b=ZI[a_,Tl*7J!,B^8t-K&Cos88[^Oa^#PgD& + jR@Y_k2b<%X*8J6mCeb#r_nUJ:`ndj_)N5j8A,%o;;C,>OR"oc2,2!mWceJjWEZVrkHMT!2(SAJB-/hT5j + jZ8,<%5YVHJM-n,=mZ3_W:/:X>?re'.q*/"l\&jg8*LI_1_+6\B-U,Z?b+bVV*OP1Wu&cRa + 0qDF%imN9@aLCmuX7nBER,EMJ\'4PKKMG[8%7>S168,?F3l#n^KfY\Qd/8'qsV)FD,hETJJ + DiN/e3_*lR*Y\>K:a>p)O7`l&j7IqbOr?_ZKdK%6W;fcUrCk<.*@.GVQBMqi;igTL5Lim9< + O.'L8h!=\V*q7`A,T_pomT-V%'PrEb$%GPE@r#Os&pZGC!5af0P2GAagjIH>Dn]8+8(f'_1gH24GitJ/0oL^Q,/&dYNI]ql4Mt) + 8sVGtj0j&#ckpa66c#iUE"b'f3Ej/I=[a.jeg9WL$GTs._DoOO@)tM!7rfc + p!X\dnLoZC$In2ko3LLKQC(,MH8?.HXN_#QWQ/*APQHP4`1u>pM75-YA-.JWGVd8s*]jc2F + UVceRcfOli8r#bVkopSFX"W")SG;DDq*)N(UUNK"8?6gFLD$2b;6'=l?c(_&bYT*_UegMgH + M/Hn9R0/=Glapu8din[KL_+%jm%>D3R1L**1G/P5mmC&U)jp:._nXf;N+ap"p6+ZTl9\JjG + "b2]/Kjhb5f+WHb#MA34n's>b13P$*=b3u%HnY+^1&_j!',GHi#,qjis3a!iAC+?f@r1_pE + U?ein)lt8?XY;Lg0N'hk#s5a9G5-,"12+-*,*B;I-SK?G*gb]ZO_HgFK.2/Z*U:(MhGG]@/P[DR2ZJ]kN#1H? + ;<.32^gYVh:%p1jXp6S7k*&.=U\,U9HA%5(6_3EZ^"C + R;nil(M#@TjsQ(9:S#l/?]C$0.a:f$6_GLU;IW<,$Oe84/O3kV`KJgYM(T^r4"f!1-3U\_k + R>ZCBdGTYp6"JN[HU7dI?XqD.8<$]Us--C6>b(Ah#7&e%fWktR4To?(Unf1 + 9_/^!]j?99f^Vf]Z`!!o + P*5\rV78-*:bZ%!S@S+db(8TI$`.Z7NPffgBbH]c$9[lb[jA!kh=j7$oV::1\$1OIcfUu/krM8f\W`Y*_q1h+`uA'Ru(7T2`(>j'+Kk + f/;?W2/^c`9YFLlUJeY:>8UZi/KqmE'FKq[9N2ZE0j'^oLDmS27*"aaIu+^prAp$[L;sj+K + J#]rC>rK2IlcqGdU'^!QZhp[_)_Nj98(c#>f]saG^DOUN8]!c+puO5g5601ljogO?iX)5mk + W@_gTWoIh!G4a^c..EV)hU(WhmYT7e)a4,5eSSN*;8_;(YKm2n=K+Lq4HattgQeL\8t/eQ;\9+3LI)C3KW1.s_c,)e?3];N6)\%8Om_h>SM"g_nnFOdg4FOZpX_uPYAq^*5b_p\\pkI'^WiXAB320^I4!qQ&NmSXNXYE`5L-E(+deMo + [)+Zb!d1``@TM-4Okc`ejWiq7;?PUHSIZ>eBP>N(]7L^e6L8XG0TYcc3.LPXC/Br?bQAI4q + qO&>=?1ej\CHj[%utVSEiA8Bhlof[/95J&sHAEPdNSQ7jD'6V/[rr=Z*:([DJV=;PLSb?q7 + P3WeG^JRq+E^J$sC$=44=]RqkP^CtJmq=;?D.K5jGQKLiFk=Bu?fD%3ss%bR$WSn/J7A.d3 + >IR-,W;,rZS@tIR#OjSaS;3KBoXW'6a*8,0LfmOpOe/u3eU04TB=hQ%?/0m5DVV1>`g&3;/ + KIFC$X'.)*>!4DsgatPWo3,o$_[WQU6o*4CX&D0_3ea\7AZd6]Y:pqVgCpbX]fTF]4BkIb6 + gM,''t7-G\5X-O`&"tu?+X'1]Mr+L7!l\mV8#up^f7)H`5BPfmDDoZ`)Q'E7178DNQ3fM<* + YX5`FW%S]iJC]e(ib]h!/laq_psodClVg4-`H]_bH-bef5O2q + &%=HMKIF'cb^b]dWlT + A!j0>'2M[/MUG"1*V-&0=3T8;lRZWY1%g&du1?WS[)1o37GJF4!V)U"19i=\$5J5]9L7GWt + a=#(-C!Zb`fgs]t^i$_Rr:]]NZT84!Zq@aiu'R>]!?nI8[`>;r"3KVN"T^$s;&,eO?Ud`6nH;p[;nUWnuWl;c+c?9Cs:3+LcLZc9%@-.V;36H2 + bg<]fB^CZ]C>M.D!K6:WAM!Q:4SWJ[U2/UdcaHp$c<.* + 4^u_Ztjh1\+b-5\G?I2/KaAu]iDT:%WK2A.Ld&pp#`b!6F^ufbMCt<<-`=b,Z'"@;U?hS?i + "+n@1KP`Vdi&r!gKH%FmemNB<36l\F#'WBTg"F@R;FV*n?4JT2h-gI8i>!hN,49=Q'N3Ks9 + )]T!'(t431%&'[k]J_nN&E_ok\-h7>77T$X0:s"+_^2Ikl!\Z$pa7J\Dh?l@XUW'M\Y\-.b + &>de:oNZcLkZ694IinisR+#L,.9X1X.g7btBQsAQ3U!3,1l`4[`Gd?N=ha4P^\(`T,]l2F$ + K=hJ1;ClJ(:eA_=^cg"'0W`fCI4c:M'.8i667:@:doV[lhj:THLC0)&ikS@jlQeHVVB#?oo<>OT8In''DPh,a"5?WDkh9ud + 7i%fuAUdWRb2n3(,h'jtPu@H944CA.ta:C,N`Yr&oslS_>[f*tcnmpsS*E;sq/o^c,h&`jb + h24`l*Fltu<]iSh^f6$plL;2i8lo%T*:^-8P%`;B2*S9rf?Quq!"9r[bKCUUId(T0(pE`Yu + b[uS+Edl;9a!op.D">$^;/\$urW;ZYe6,R$i*LMH-J*NKR8OK_D3bOlMiq_D,j26UD9\_pV + 4geXU2BT:kPLeU!mCZE=[:Ol22!.XYCWaIC=jH1#-3TrePJ+s%8d4+`uo,1)O:T'JZ.LA.6 + J&,"&M83n20dS#+^=C+DJ>(C'L)s^s`iO]F?Fp9$nbq8/b5-P(BECYV_"B*s5"udBGH=@Kq + aAU7c1e)C!'$6bq'q1b9(B"$tZaBIFc'#(V8sm]\M>%/U,j5gOC*ecH&$JY!usGT#6L"$46 + CO>EC8jT@L(+D1rt^'8J+RrbeiA.7&l2*fDIBGmNKB7ZJDAloq7@fnI$bYU)(%)KN+Qf)H= + )0Q3hJX6)81=Q`=""Z^[r9,#%#$$5>+D!MM9="^K^rd3DHjlS*JWjJPa;HT79(g!A0JZ$)N + rpUlaM2C+&dC^SOJ$%jrdfk2(k.$ka;B5SLQGC<*M?ih"rs:E`$U%ikSS,eL8Yr&JVJ.7"r + SOlM?Xn?@=1h$Kub*7JVAms%&k + T#_f7JY%Z8k(0+(Op60(B7^>3TP&CjJXr_WfR]T,):X5.2DPJY+D3_>F!,*ISq3Df)9.5t8 + /]9qS/0NWDkL_5#&^)Gji2k@BGn1.Rj]/YJXZ=R#&>>nWWm2\AtA@@RReh+D'O:?(bcW#$d + g4`b'dfkS\2fQDb[8Sfh\Z#_O9jq@\:OL\`!?U'LN?J"iBP/;tfEnc"Q@klLdG+6asJP99$ + Y^6L"$Eb'Zs-i.M+VT8OPJ#>g5H+56E!ed)Hrhq;=U9(I=lH0%.+6rCq + YTN0I*jR??6I`IdlY6@.+7#'g\f^7I5.iOAir$V7lj<[.+7(`]`#n>I?H+_DK-\V^m&C!.+ + 7.DSc6)EIIaBoG,>`lskPJ5.[>4WaJ$oUZA`u%3ZZ+bCmFhKr+791/i?.J2ro8AY6^6^o#Q + (*B\juXrJ%E\tY^a8lPIU'D%jCrBTC4N6oH37pJ"F^_H]NaK5)8'GT5jjZL\^iikP0P"#U4 + e\+pL==0Rob#re$"&&0L+,J"]D"Koj.i6\p8BU%/,:UfBKsn + Ks$m1i*=qdf&1sLdJg')R\<\qPuNh;:dB:["XnBQA8,D9gl^WI-OoDr4qX#Bj^2'nek.Oj;s'Gm,I?4 + 't5M,]1?o+4%YZ;7N=D7_CKDgRdp*)`RR/^X%fo*,$2TJr+s'u61OcT3.5M8$rAMb9iZIW@ + +=fD@4KDh:#p2WCNf`,Tk=eCd2$9uTn6tgEXUpRS!e(T'`+66s^Og_:?)T,`oNHq`d!`hP- + B`BCGRBj8ef%XDUEGd'"J!:QE:trU*Db]c:3!TF_8,6(*j]$_pG7NT%;iG_A6-'g=r`R;;B + clH=bIM7Bk8a)dF9U`TQa-Ns8,7XVm[;Tda'@sOJW1c64F`b2raais[WbL:h`M-oBLhln+6 + JjK4BI^:hJ:G`01=NTo56e;bL_'dC$n.I_"aN6aWK[sJ"5-;q;5EDIIG/N^GEGccM7E%0BN + *ra8Gu\'EUGXr:l?Ek!.1n$%WXC#=/VA"HXK#5bn8[O7X(i@)O%;d"2ir,VD=.aT"V;r=6] + 3;%"Oh0SWEG'p%uj5LFjYdD@n68F7F]f`+aBrBJ?DpE/H)K93AR7Z[3a!`Y,:O]"&2.E+S!8 + ,Ar%lB$LqBf.5Jm%9LtN`+#"Cd6Ics&W+$]/NQHL\+XMOnBV<#7To*/XI=cY;gnG + c_qrGbNaR5?7Te=+iqCbHt2]a+E-S'uP"c8j'.k;reglkk5VJcYUc?df^m`#M/)B\ + C=KV + QP#>318,Y6@[,&'sH?;m'aZXN/rh*4?Lu')i7k&"4^lS>[;lYOLE=O1mQ1DrU8,ZVlQ$TYp + LG;oSRtHc16Z`s)QMn,XX,\$a*@>#b/meJQ4h,='&7utN0TSQm(LO,=_o\3O?`<==3EB\ + B4]'3],-.R+8LgMAnf"=o1K!i;kSX&m,=J@V!jlMh2*k#n3?O,IaAu?J'\'#adH-7+8RKWB + ^Hs1k^J_.9_s\Yr@lTspXR`p.o5FK9%9h5<7Kua<41O`9Ud%\Cq@]VlUK^4WV^e9D;/B]SU + [kY>D&pC(9.>)P2g^k=`S8gEiR"W9rN-8XK:SSX_18.n.sdA>21UmG&$SkWqujWe9A%IY\/ + ]pkDJ,+^m534ROc\$Z(?oFF`\ + *#S'+NmR4R@._,g;\WE!9BKkOYUkGeMK^6#Q:2GXPE4cHk]SLIgjD*m,JEUe&Ls+e7%[9O; + R#3Md^IYNBaKk*,]'ALtlRWO*$l4gt^eQN#Nc)qTM"8Fhp?cDumB5b6aR.-.)9uWqkST[SP8%6N9_:@E1W,4cY<$tl1iVFMYLolpoP1&9cB^b,8ngtU5ttql88?s2Bc + NJe&Id)&%8:rVPS:HeHWJ49j%=NBP`QG*=ZUtNG@&5g8H@`W3P@6`qnm\]QKZgTR%VeCLDW`^V]LZkOd^J?FI-==^$k(pJlfTgZQAP$ul1!-9Lp4Z-g? + QbplOL<49G3=9)_TFQN\8?1u5d0@N\'+i8&mdUqY0c#qr?BYe11(OGN%gM + O..`[BDa:g?!M.6#$+VW(_QV+CX\(C]/ + kD!%:lDIO,spMkf2WV4[fqY3NNplCCP9bsF>*hfd+B+LU10rAV63,!.h)\91m:UFjk])/bH + qrY3^.U^eD>o`6@-?%?RDq?#,k8j$K*\ZMkDs"#h"Wm.$+#!4-B,;YiY]a% + '7K<+YXQQE#0[q=!?kmG#BUf;CM[1/9c+gYXaBlE&T#G3dFOcZ-D^o;G,iAHN`n<"Ys,+P$ + 8*q?NoWG"tNO/Xs#.[-9SMR-ZF6ZP'mS`JHumAJ[1*ABIPA!O!ucH.;1dlX.k91TaDF/$jWtKF+Hf;F&X+4Idif>14VCG-n;e)9%hr*m2)C@T1WaVn&OA6N + 7$nQWb"jkcZW-Q#bnZFJ("6Q[;e6_CBblQE(mgd(Lt9M_%]D/e"[g;Vch8#3q9@e&[J3L5h'edeCCfdr1YLF6_CRm+AT9?8MC`F8X^prn7ti?Si-tF:R%:&DM4C?o/m3FD\!1u9_t@OiB#2CdU'917>g72@l@G!/:&5^c[$[.%^=c6EPcDe4$3AZdfoXuRmgAOn^n + 88_!)c;,liF\,VZ8WI-amU[kkKh?NF9!3:E%XPe]PeBo.C!MbJFN&!:a_IuNC@otj.(O,0# + nAl-l`f\MG0*Lfbj;dc:KGu+f%@mch!Qs-:j2"VG3[&O)XkmaDbP'^QMg7b0iGIc;YLOh(C + sH2&sq2catZV1ml_Cc9lcLrEmII$'jG`N_/kRdp$ZHNDedSL8C&r2pTo\N%1.0:%f!5i3Nn + as=(p$cT_``2qQmGNF1$4o6[/.:GUBUu.UN$q]%-8q4Z12AFi`/,brulhD`-%D/V)JnnCYm + _?:qBRD4Me$U9%AeXKASY1)/K#iF#:?gb$VlD9f?9'X01Ah=mL + ]`mFU(a,'2[!IDUTZqp@D/4pJMM0;HElsM-Em%M%d9Rno7$:,37h56='JdfNu;l$3VR/hGAC?bV&GaF)TIb?:U6d + ]X*=tSPB"U>3B]LiF*VcXVq7JElA_'+lWq&,HNt4M0Df(dCO?@CQOjU5\TrT.;jZtq@(D&A + #BQ9Z0k%VXIi4`*<[ciZ7c0+WR^,o!RB^8f8V%Ld[5\slX(.Z_H"G[VJuBKVE/A2[hh.jdS + ktnhg$OHk=._1KB\E9:\cINYRcpumkmQaL'%"BS(MaE]LU,pTfRZdK*MW,s_Kn(V/2rEL#A + s70DH]bPN)oD9BUOem6kX'.M2>'%4':UnociHTW.>E)haX^SBc76eK$]0I1I5^1)HRh^=X'7BEiI4F?a(Y_8Xha4g3YT#0CZoq>6oL,?`W=DOTebj#bhl?f4upjHr'[gm;h]%Dk7'^k>7q!C/RUY)%&pn + (AbUV'^,]U5g>#s!o".*",2]VFO;"OSWNV\Q5:JV`ZNt5 + Z[i3El59_trIW]LS2&Q,MD + H>eF'_),nMcrHbg=HLpfV.]oqFD-k]q?Q0T)=`W6lG7--(=1F_3Ji/?]@ + oVK3_-!5V,7+X;-:"W4\K^a:VCrO]sBT0oeF0D>uQ/[1jeP.]C4=[+kH6f^NEJi]m3VKI5\ + _?F5!0cR\;4k^LD;+`(OuIKY4h6a3]f[*aV;9+8h8rn + >6Do7ZPeGU22km8lh8UJq?5j&p=_-3RBg=[q!.,AdS9nVm(LS1&I9D*U=bT1fM#t:>@@WZm + e2s(V)9NX^Ze,/ES?'"t43KXlJGcsc'PGW,)k;;r[GF\joR1=5AV0a1jE]QPg#36AF(`/KB + Cqm24+2eXo]J719,[Y_>lAB-!P%+hrt5;Ri]&'<%g3H[";!ir&3U.+`1V\&+DqH'Jd,_(5f + =3e":IKsYX#"Q#SVdmTRdq&6=0j#_#_nJC_.He"jWckW$!Dm#_E*@E'Q5k$NpMs!-H:)$4$ + Za&P0,%[9g+?&3o(!8*gi`"F\pqO/O;c#l'!7m;PS&&aKRG5osu8Q.cN6Jik09p`mBM"EqU + %O@NOfeHC!RTT4gQSd=J13HB(/,SA'"EBtQu7g=8Fhh!e[NS#Is:BYON)c0#J&[;ISm%B>O + ,?>*"Ji*4@7[+L["DC(=r(&%n#gIrW+H3fT/"ZrT_&3eR4;!S*Jh^Yl80\TM_LaIhE(0sBI + fs+E(V[s7nd)jZ/te,i,3/+p>JV*%7B-Vs5R8NFJegm#@Y,@,8dJLkKlRH"TS6h\D#Wn]3[1n3&W$(;U%VR!GJdsW;#WNrZH3cGPK5E`& + %>Z.XJdk6ndo2ol63oB?K/*]fTS"P$Jdc<8`.ACG+Mh?VK),a++G/ka/g,c%N.MF.+L6S,# + U$<`TRsHT.\MBf#UuEuQ*c,M-m517$0WM&EM.nu#UU[G=pQE[L`hk4#m_Z6JdBMimXL%K+G + PIR80EP7TRm4N+/"2U#TfXijqNCPBHVDK#:L^7M4fF2#TFn;WX'LcJY"uU#"TkGJd)K'?^% + .6EX3GRJRSm"jp3)/Jd!PF:kAi6+AJE"JLUqVTVtH]KHKD\re@'/H2`Vlkan<%U?K`\J"qe + q5(B@UqF5t1Vts2mV(#SOR%NVorgBDCWW&BZl,ipqmKr0-TW"dbLjuAdqN??-BDS+4WToA< + WiJ6;XKAP$qS%H\-i1m(XK+D1[\)korjSNcM>k&Xl`'lqoF&h8^9B!^!.V4cq[.hWkPGmAZ + $hNKa0I7qrlQ>L\c0gFm+MB`TBuT2d'RX?;Lc(Zqc83SVu'eM[O:rbl*d_+Eo*GUiVq`,mK + rmOTC+ANj1-dHRcpC3Jc=o+m]$3OTC1%dmCTOu_Xa>f+o_)*mn!IAA,Q&9,MmfDRIrchr;7 + )sp-6mes$,QB!5TobJP\B8+6Y,&T[Eaka8-AikY-K"=@S"kar<_.r=9G269ij7s$WX\'9Dc + a%1ubq7HhsFUETVr8,>.dki@5gZ2Ten.R-HQ%3pr+N'G`]s%-`!-BIek'bZC&8uZ)JO7]eD + dcg^(j8CZmoZ%g_.VDI)XZ)=5gcs8Ds%[)&3fiq%*>?SB:T=:YO7`W?e0t>nj8DB,obSbcC + 6)7B9hUQ?/3hY*s&3G+:65'4,o$c^<2uaDWe(/La82n[lF_VI&cSln.^`=Bj^+#@GX'aRs& + ^NE@?:)=5Lqs*=Xu(9XV(Q>#PumklVrA9EW3C:qV.D'F`4Dtb!4K#s'4U_FH?+G5M'`&?0f + ITY>t'(80[mPlgTD-iVpV?.fs+;)2'][rkc_Q%Nk%&J$C0r`@%]%@gm(31'.Bpa87&&m#Z_ + &@K+Ct.k5+h\X=!%@R&P=s(91IS.G=85M>-6B?^F.&8$aFg,hl?D)J5ungm#H_#GWRg&UXY'n[.'(eE3i;YIUS^TWt=R2N1Z?A\sJ!g?:A=^WIGAd*2]GH=lbn + 0n3KDa1\Q]Ue[F3o5/;]5aWJmi/A>$BFoZd@K(j$bSYtoH4WCk8I5sDs$K0 + P%RR*m$XnE;L2b]^U;?heL\`][E9\N5#_`j71e("jVO<,hk8"8te%o + dM+2>`Y%':.iW.6pK3Es&@Ea9hFS8.&A2dPfhX.X+C8QV.?Wlo#=hU=`5nC;#a[NrF\dVGq + ^QZ`0f!tA1cd>\-MliRF='-brMg^k.:%(lc=RZ62nQtEql.?q4"# + ^PSs.lT0ld348,F5Ilumj(aoOc+,mL$dXFIP'rlaM:$_d2^M:#K[7G7JT$tHUcouajIB6*EUadP#?J`74:s'TX^b'@#)f + ,Fg*oget>Z.;5.]t5V1N\f`jNBgaAk$pX7 + \+iJVk7`a@]V\K?#+8E;]iu'j)0qtgO$IVk6r,n&ToW=Ch"^rrNOY-n<8'*bD?VBaU[?e\b + R3V_]ASeV-k'gN?9=epMr5"c,->&b#SU@WKqS6`'s39673G1q>40uO2@^Fj>kKNmt1_fq]g + (fdVRLVRSBu\)?1'o7/C&o.8Se/GpK0 + q_hk#gMQN@E6b9(W]Jug0`MK?LZ8,]@;I2JZC-8e2]Yc1q>: + ZRdc^.#G]c/W%dkR`[eNHP^]kc6+C36e>FgJ/U]t/L&b<;cH5H!7%`\ZR%)r`\HKX>KJ^0- + o=C5P3%9.82].-t`ViMAC&:d3Ts)Ocp]"&kZC<4,s$.@IIj]QCQD[Paga5AHi1WhN"I?CU7 + f^T+.N,Q*GGXt#jZWiQ0"aiZ+"J[UE\2OeHs6F.[^[7FeF*G]RP":2&rgFKpo[lN>)96J%u + ^`GPR8Fq*JH[7WX&Zf^5/<#e%l0pY0's,OKXQfAe39iIrKu'M4T=WRFbD?`sME%saXf;K*a + oo6VrXi/?dJR\sY34X/j.%![C>@,?q>bPH[+_4g:)3Yd-F(8OGRD[_sC%,YJaq>IkNt]0! + r#Z#WOCPfUK5:qR1P`3UJ\;]j64`?g_f4EOB)6=$m-dCcJf]XuD,6RnsC/Bq\O-DSsWK.s1 + W18a>T]jbupFBl.uFYfJ04gBdU_qdcq5,ZjpVrQ^')r\L^A[GWp1jnK#@Ym9ikXM=@7^MB9 + ;sVHPm)J"_`q/W="7M]%n>Ub72/I@lJ=/VqG!B5U)k;.;I!Ir5]t^#3S.p?8]R<#oIPsTD2 + GqRe$in?g!M%X@WtT5+]S3f@_*Dgl^jLs/UkumWMSQ4J5e=Zg;g-7JGR[U_aUAa&)='jF'$ + 36LT(E_uZ1?"Q(X)"=d$N.n[Ytb1&"8j=VVR0/#,S\g4RB3@$sKZpR/1b?QpJCc9I_0M@<(UHH+ + TUq9"$]H`*C0R>h(lorVj5KjI"9H+J;4MsQD`'d#4,_X/l>"d(e8'Ho_V#3fh]>6T\be-5K + 7,6)+m*DNPd)?F@`>W.'.k&'Jc-9J=(Go1;Hrm2hfgV.Z7ASZc$sU&IFA:Orc0Sc]qKU9n4 + X\2-7O7"`1up%njYT;W:2FOfHtTC2BXFPt^6;71]B^A&lmWTD[1mPe.$$RqEPE3.RhTqY1j + $Ju[6IQ\260tPMd;2rq+%L^:V1&M`-jl)AA@d;2J`C%F94ui"%Y`*aA`=6A-O&Z#:k.>[hO + ]Y.*Fl_N'P=@*\<=:jGuRrk.q,;\#XmJ1OY8T'M27=;,`%^F+XseR6d(+;4E>,]82M)S\a* + QdGp2&"-*oAU-^-#;Cdns8HqLdVS[/ITer^bN2a[_(IG.g\[.gKgR]"Q0$=)Fb6fLf*&5Vf + 189FXYFRI1Ho=$J/k^jW99hHIe2k2*3(qIXbM>9B/3c3^6;/(Z9I2s9KL9MA\[b\]d1g + di/Z8PGpP9X7Vk`(WkS9dPHf1*afOK[DoMBgj;r75WL@<7OE:ejX%^7<:+^>LQ$1eA\NDZW + l$EN8?^m4&,od:).*Klsd[0ONpO1csokdJFDGM + c_cTQ6n8[M8sS\J_0[EJ>H>l%N[s/N?72A/?=91'ULd/crhR?\5S.(]8/%]Q7\#P\AY%" + g>5u,hmiiOb"Iq$eKnA[-95Wb=q)l'ALT7M/(1bp6J\Kdh58WdTi1ig;[OmN)YlEuVb:NuY + O/Ks"04K,]"6o*gFELj$aNFsZ`q]_YiE/RI)ER['AKQ&41i1"K>-*r8)*E%]H'T!'hYf12'ZT0"hqHc(!3=7F+]Q>!8*V4fO1l.m1MghG,TYZmF;145r + Fdk<;o!em7"\CZnfHleUB2cJ<6C"`)EfCmr%SWH7U\qc?!EoiEF4=#4&^7/5A2][pVg2>^f + Pbl&GJr]fInf`W<=;o?$o_H$=H*e3>n8JS&Q'3h + _)75"4c[o_JAG`I8VN;SKp'iIL-_kqaj$*uc'Hc3Mo#"E8emcB'q3PqO[;-mY!nna>UN\OQ + bo"9g*J3%1Rm)e/6Ypmg1'Wj"fOI51/epfdj;'+Cc+8,[p%lO>4jg8`&YEa]A"VmgGM()*' + QG@^ftA7>BN1mF@*Tu`7q+UgU0G]bXXbT);F0j7'46e1ecE,_UQQB]QM0BmDYq#`j17rgl' + HH>uTOd<8>C>7>oX>X4>aZgdT4D>nl1+`Ecb,?9R(P^%C@@ARXD;@_O[25"eFPUIiIrgch3 + %:Oc4*a*ptB0$6)S + 0HY#$g2sJB8rNDCa_;qlJWGXY1=6>9(?QlYV/1k;Zc^PN3q= + Ke"i]gm/6/.,?Bd-&VCdqXK.-Y=nBm%2',YPd3+CaEX1+k68;^Ubie$>:3eLN4]>hE<,2sO + (@C1UP]KO$a4W$d$V`XUF-m]7f85Xnc+A1"Nk5-\D*kf0a\7`F4Rg6(4iChn1PjLoIhA+I$ + V\n(3j:NKaTF'WS3l")YYAOQIXa^u:_=EAXN + gNW#'qds+F\%E>4f$@rfs0r75d:NEqL=4a5DGd9L;8.!$L=Tu`*p5C'B0tKmQ?H.aG=l1d8 + oj(WSL-5VI?;X@C.t2IBVjo3,"jCkc4pU[Z&\\NR + R$MYhne^%7+\K6Kp!7mhV/;?Bra#QHZBLBn]/tAt!2r9g!f"4I5eQ*hM/>bsb:Nn&D']A's + =Zs>:=p8caSXL'njlKr*Hr9.o)l*RV!c7ouu:deB[g]D,,5$dVk\sn#o + o"QWm1%r7+iCJ0nn--K^F3]BrplZ-Gg2=ID38Z^Cu@+'i'eRB<#k:2QZF17;>lo"L + 32@KOPmMki5,rpFe82ZgtDtgb,4%DVVh!Y/+^ufmMLI=dm+07AdJZkVBC_?,$G8;LVXUb7% + =i8"A$NTM$P:h#`aK]@#2oE&JkR=5nPe=7'[)RJg$eC=<\@`K:B[g:db^(!S-K04@'JH6a]etFb1VVLH$I"#][&a%M2!T&D[IIg)u-k+g))cJf8M\6UVL3">`= + PYX9`-#\/H'TS=:,K*qf8_$eUVlk$]1/`\%.W$,aY=Fq:Z0LE,XWrsn<$?/=tOpFp:'Hi)( + +Zim2-G^"M6g&FWcD?#[a6Ynd + &RYBH_VP(MFt6JeogYEIT*b\d2L_KrPD^@-AbGJegm#@Y,@4Jd>GNKlRH"TS6h\X%a8l;a6kDT03<)93r<H4=Cd6=c5Y"B8+!^uF5LLDK8AkBO&+l)NdUZ0Ga[%@% + @K)N^qH+j(C%%n:_&(Pb@SDFQ1Cs-)5LWSF9o6D?-UIa%a80s$l/7&hU&LYhqL4JT!C8,&& + 3nZ(s%u/^7M#/35Lb5`;Gnr9.:k32#Pso3l?IfY!WK$QqP54uRSBf9<3$aas&J+X=V(1=5L + m"<T=iVpIpqXP`odVZ1!n@k7L!$CP)J$:*q_$BiA+7(1oYbi8TR#q>W,Q3K1p+/G\_#In + 3rJj[n4NJBR-6MT=J$OY)b(ojF+7-LeZO.VpRA2ZQ6iEO9p38gTnGd/)$)073&jjZR&S\$< + M3%3W5KLH7C+WGd[?e8Q>3RCVjJ79g[`6sh1MCW"Z*uXNG!F+4s!R3]\[QbQhV7@GDrj*2P + nF!pgae%t%OoCWk9/gn)#l.^r3NqH.ct!jSTF4H\rZFM5Kh.J05d3+\sHI`?$ib0,Q,NAo, + 9j;Ha\+#jk[Q#IdbMEs"i^5d#[cfnX_8QH#GS']^oSsc'\MhY>sjNLU?dsoAG!$'RuWmrc2 + @%jaAeap:g:\r(b?54(J3V^N]&DTB#p$&./FZdJeL5q=0"I*X0@3*s>u.ru^G-J"SOqJRHD + 4K5buE_AGUma8-MmkZiV2Qr+1K>V1[!`YbFirea5B.j*u`*(+sp%q5B%La_V>UXB(%a8/)Y + &KW7:fUijeMGG>Y7";$mY8*e]>R7T\0I@(DR8Z.^NC3Y&O7_9ndu84)dE4L?B^m.=dX`M*% + "G3Y!?^ABKokjP7EDkd5Ld4C;_gg58P(K\,h/?JMPPa%Z2Ue8'VUbsSlEkC>:W_;s&\7Z@* + :QA/Z$CsQ]glOO7f53esQJ@ZOUD;Bot0JAB\:grGmtlc9KoSe!a3=Ce^n>2]'"(?R,MtO7i + B0k7m'L'<64N`YLekB`DN4b';68mlkC>rL^0=J$K[caQOWGT[Y0>:s'eGR@+"dU]$\08gd' + kbo:=a+G;R`OOBS-9Vf%ePdNEI9*k>:,cu<@djf'+Rj2IA,g;^@QJOjjrB[B!e[j7>N`&Jb + J04G>VcfYTNP`]%;9!mG5I'1 + ?NH3&,QH:ln+!;miKFdZX38/rK"6B:$XrX/8n8Y_gE^.c<9Huq;4?13[[Ve^ENVlD-a8n&) + u4_;pK^I)#q%qinZke'5p[grIRE9iET$8EZiRD\S*i+_BJr$/sXqtd&%N5PX:48OT2rEK(3 + /jbTb=QE2 + (.uiRPs-E6D?d(<(C1]oo]k6L.brMsck5!+Z1QquN1UMYpI.NrmS + Fb(N?&Z&Ar'0g#L=$\JJnVSnb?9f8+8SK!kuoT+FMg=V1j;^C#*VTc'2PG67oPQ+;V&6]0l + .QSZ"kOnXW(TTVp35T2a)IaFo-R=2(cXDpBe,4cH:.=19cub/u!A2 + $.NP_hgA?NsdY>?M#7sNjJ!-h-BG`*.9DHttonXqEb_(4FI + H[0Pp'L[=#Q`o>&m&9@=l + nXX'3fi^a;/Y.&(XQBeW7YGB#:G8bgpCSeu`igC4L1 + oM&ku2DbYc7:["dU#-'EIfd@QgD4:L)ZuV&;+>'O5NNE2\b]IULFKN20/\p.<*3P'/Y6Sgu + ,:_MdgB-[DG\[_4XlqVom,&@>Z3R84P"=Kn[brbC"Y2W]MaJen'RbuYiQu,C8*[M1CuVAhi + pJT-CMn[GG:2fDJ^-[iCPIGANe6[u6#Kfdd$VBm9"Lrrk+k3'mW/`9^4r].L@0R]DM758]Y + 18$^*o$+A<,oWT+(&ZMQ?b/DQJc#h$rSfN2Y$]0^-?og;6bWm"jrncI!0(2NQ'H0J;4DcJn + 83%)iMo`KM#)8M9Ilr=SC2PRMUgD[Ma?%e28-a@Tj^dl?n)Eq3'maiT(U8VH\([e#PC'eHk + `%)A<-8mUm\oeC"%8\iTm`:aB>p.oB=D"+#)q)@bcT+?8BnVGPl!mrfcprX6F&!\$7,be!b + 3\R5&:T#,.MYbR_UeO>uND(R8]c7R!*(I[_O3&Bfb\ZapVi>NE1#1B4LAheGr__lu1$nnen + G`_+s%9BGNLq=&Ub-Nh+HQkN1(OBU'*0j(F]'-\-Y">3+X0`/,)::bb5QW@1BTBk"*-GJ;E + h@`UEiZ),`kQD0MJf*"Q6Rfi<,PQdU<=mA:AQ5#*Ng-a7ffHB&a@[-dFh:RE=Xo?>L2sI)o!8]Z[YL$W]h!o0&A]no`@Qh5/BdO?EmkIQf*uE + &%C4j%0];89jQibr0$ru=&Fb^!oYU%Am8F#PV%XD=#nW:/Og>W3',;*N.hR_Ln#laI:BjiS + .5K:&1C7DE;)L.l30s(sa_?/[;+3=(8MnPM2%6dYdoR9*>8=JNRe("4l+K!;Bf=@U2[Reh9 + ^81tHPa#<([:<(/HY[mK/i=UAFYjDEL/\c+Z](b3\dA(&Z4#l3n)K\e0^_*Dmp[h\n]R*V< + q=6dE:#a?Ms?Ar@ul@&_d2ghYlU+r[O)`/VtP9N[jMh+)glqV^3GRru`usF&3(BF9pVt&DN + rZ+cmQbe/I,H,2E>K,-WRAoISi11u;_<,LAS!'L(Dn7Q3"o,k+SU-cIBN=!b(SI8F7Wc5P[ + TC>ra@#4cg?13S22HO$j+#O!$2.tL5$9aK$B5r+]3;OL*/@gVR:6aETsEiVf-Gmb+27P_L^ + P.aM+NsmY*8@$DIZHl4)V%#hr%?fOAFP/9!\Ls-+lk&S[Yk'MepFiK@0C\uq0`mH!#/lP_m + ^!Pp"Zm<;)\KCtPLC`h1FEfL9bOOTnIHElD]Dla4#?Ck;f=6%D_bNQ:DED!<0'B]O%r1MHD1u9%m9Slc?7MuTQ0R'7!C.Cm5ZRU + 6&.I<6'jrj5@!!7@E$!f_-XehZ@<=!TcnO<33FXg*@WX`i0LFZT.q:(a"Vs/Wcrie\77FXU + iRU^m<`"9(<45VGA^3:@e9PYJI:fWR#`_\0(3N1!GtAKQ$*Ihh2MF`rM+TCC$I3uKmP*lkN=;K!EdG7@Ie&Q;IS'JD*r[iJF,+]NAE'i.7Uf.C!(0ia97(2m + D8pH;Q$5ut1)(QWO][nP-\LNQCH)"iT*ondiFQ$.;6G,d-;m54[+G15obGOr>@F2"Ks_`Vd + Br3a:&'uN1/D`sB14iZkQ$BY7.kh**-?5)p:;tenNqd/mnEte\W9s!))&DN)s?qTA4/]f1g + cEDC*YKb_Unhf2r'4OTOhX0t]D>RfW8Rk^ni,hh+oMg5Q=L>;B7LgXI[SoZ(=%;U67lFdGB + GF%36Hhk%h58CUWC9G:O/N][iC^/r&T<_W`T8C2<8%cD)sdWG=?>CN + S.n;WSC3_hbBACof3!QI>/?!Dg/[:\N`7G3)Pdk`U,U`Xs50:BM50*H1ToE%_lDFXqF1CN/ + X)nkUbmD_T>C*m5'ToFk@!Cr^S_bk2KJ2QpB22a5NJB(j]$pGY`HG>D;DIolAI==O\\D&X@ + XNqorRG1E")'qD<m*o?UlVn4\N+n[4ZlGBr,#i>(RH.7H[4)oW4?%PC6b(Wgo=G`+,[7K + Zi$m"G9Ft"\oQ7=hc:6:RkH\[ + J1gk95/uWFLR[o.!BI!=?(q9lNq`_T/]Aq=nrDjNM%+jLKXoj[[j)dBSfn-%+_PSfQj2NI; + >9#C\WYpY/@"oOe[*Hm4eJ*N'W=7$H0HPDJWOR1E=D<=](-#Mt8RApr.32O/EXWEE[XIM2S + ;%;0#iqNesg8d:76$Q&gdFWNgSf>,.1JWK>_0p^rDYD&'+8R*kcFG)\/3uf_YPSLg5R7V[h`n(aRf6[+9=^+(,LAn<7YSh)#Fm.N)r7X4AHoZH*q0bN]JH8so + @$fP4fA/bPi30aAIZuL7e2;^`(UI3=6l5eI\,dl`21TdI[$VAo'OKbfB&[#pSCeurXu-HE4 + 15tXSaK">1khN?Ogat1KJ\sIFIRra[C=de8pFE0fNPs-M2!jtL311bX6mH"F%j)f/XUk"P2 + Ei-heW]=\sC?r3]/RL0r),M]=L@e2$33.%ds57MY^=k0)lOp"hcs*N"mW.;)7[Rhi-90np5 + LY\pG.'m;b2H<4M*k)!]>Wqikk8a?a>[$U8(!FQniNI=aYIn4+]ON$F"($N3 + #CMaDI+/aV$K&,DLLAUT@2gMA3L&@1R6qj_".M)US>0QC7@MCQb1'4o=::VR\$X^ZbPB82g + r6[cEQm&aS\3F[=>NK-2b9;s\XRTaTs + W(Jq8cY$"2=>YD6Kf>T\BQ$c`5pi62?QCHRE6lQ!r/u@N.%O3MPCKVaNPFHMjgRI'9n84g]68['X,Gr4PQ^_']%\A + XcoJ[U78LJ[RCbjgK(uG&t+E4eBr)T+mWa:ntM.MpF;B9"'qn=C%S[L`c]e"8j/A#/J':0O + -(3$A!e:@#,X@-NaH9^p#L<$jRc'J=p9e)&.u.P=ajKJHPlL"+J&CO>kt;#5Wnb?uLi?hKK + 'K5i/_(U^cJhGu:5Q(`U5&r#2Oj>0(:dS%CH:S^tTDY^'Y(qJ[Jm&8/k>@")1$S0K+RnT9Oo5TPW7"1'YpQZEs$Y + 9F[*dA&-aNL`7*6`.>/NCa^5-B*2O,fC1Zi%=>a=Y97Vj)X7O;JZ_!%27sB8"'WLkSHp.Z# + -rf]?u$7r%:93=5h3(rA.;W$JZ>AXQl;\'VtRpUco!?`8HVi*e6e"ML'F<%PNWlI!2*lf## + 6:Og'17oABYmAP6`$YJWe(l.ghq[U'=2^A5bMWh#!^2M)#SrGW#5;\O(]C'.%k(m#!=#<\ct5h8/UWCNeeP + 7JW<8(rCi_'Nt\o + TORHr/tc\5"te9E(pnjP-lB1>MY-`L.j/<$"tCa)RKb3@8/S(PM?k&jJVhpG"t$!P@Kn-O@ + T@Z(M's4&JV`M,N02cUpkXKWBGcDRLc0P`+3oWo"V5O/rJ#4UiB,_UU5J7dI@0,,Q/dHoSPl]4A&0o + /LI:cVE`Ljs%9'b/!'>.5LS=j9s"$o*`jlm4j]oEcF!\nWQU1UVSGSn":6F!dLK*Pt0`Nj0r5 + C`&?L6LK[s'IcbRa;%5Kr'c1X?*Z]Jk`p`B=mI'9+5^6>K[cER=1&&%^%-g:N$_tnjQTRWQ,rd,ais+#&("2L9?!mfg)5K!F"5o:a + K+LDu<:i[$h!WIb-q@nTZL(_@J;[VJe+s6$1J"gBNMBS3=L9TMS_mjlLL\`tPkidMkaFt17 + /4YOO/g[&k_Ld!qs,iuB;>G + )m7#5"ER#5L_su;,R`daB9#tA6b+VWb?m]_>^@.SpK;jO7h6ak*4Gd'5D + DZ`V)OJkl5#]MK$jDcT5]Dm@C=@J$BUb`93Z]T$u\I:W`Q"R2Gs7U]$D'8d@ZG9adZ%5)6I + arm"%24/0)[N&3"Dd2Fkb+s^0dO8;L@kt>#k['5[U>$ag8Md`K)e[&2#F&(J*D5Y>YU(i@O + 5O'OaW=A[<<3iT+$KHsbogYq2=!$De_.kQU_9EMe]iZcuTQCp5^DI%YlGrj=XgPjp],Ka@N + 3g);p"m@5])(o&%=g:TWT=Zf$XrBrMg@$G`uCe$p4rU1ZF4)`^)+\"%WTpF6iPHd4%fDmKr + @*=M"dH8:jh/3lo*j-;R?;g!RCYWS:2A+\NcF4d% + `2=amN;8Z4D1YX4YuCCA#tE7O8T.julCCmI.?e8=R_?)WVUqpW#.AE@FYT!s2>E4pOK]o$qqRl[I"4K"D!(sC + b`gYXh#J@%(_^+#A^3efgk8#VVCW2&P^)N:WKNg_auLjMn(qs:::W@:eb2)23j6gtC<HoKOf/>;L5_=+l>ju=<3_.q@>C + =>bDL**IO3.r5&H2fIdSiF:`36R;tqVE?@i(>M^+M3ds1aF\G&2%ds3GK6gHmgVI(Ttmt\[ + `M:bFQMmnY,gNU'mY-';@NE0e\D3YGs"^Be)5kW5Dg=UAFtd$`k"?XZGN(0PWPNN4h:k41d + #L_s7aW!tS'E[j4>N4-W)e3_6M\`]M=:]A('g-saXVi*^`$f.`(hS&hr1a7K- + 7--TfKg%lc7VbPnRnCtqkf0Al#]+<@]PSR?DEcio53].4r,Kb%nMOhI20#un;tNRioc&PW. + LTK=gj1jIWB[2+5I>hY<.7']Im;o65OtqbF(iWNC(&Mn85o^_@Fdtk#"XCP*M`rkWSPY&$: + r)C8C?Efik_3kO21+D_#0=dUlqg9mE%a08Q6Bq@Id!1c_"I0_0hZeM>=uVe[Ts_8_],`KH8 + 24*m+foarDPH/0U[f,-EGZ\O<=*=U[W5W5dO?`E_$6CtPVtf + s<_X9se(u`*4TG>Vhl&]sQ]\ac".Ai^OXB-j8U#2!%1nKLSm0^.[T`PNSA3Bf*bCW;Q"]'2 + LIoDRCUJ^@ZBj*:b$OF$D5J[>X[6oC^.Np;`$&a.)kEX8/7nqPnUF[PWHA8@(PIJA;#5SD8 + _3*.u$UdD.fM2Tou/HZ+jb#KWD@d$o*lX*"jcNPNbud,TC:o6QD'P!KeG;(I+34+If?QGHg + md;st,JF5^fRmEj?+68tY`1B9C!^Yi$37Eo1,ENH^,"b6J8l?0lA0jii-6Y2n/sRfpS12"R + (.ue$b2"U/oI`02+A3D&9-l:'9JX1f.SF#'bAB*tUc1?F1eXW)9=6dkr&^+l4Nm(SbPFFs$ + pVrU5bp5&YoC3bUrBsV;++[0`,*Mk"NeT@b/B`J7&qBYUW#XM`l-rb1/l:WXO=c!.a:Xcc! + =72$s#M7Gg2IT9r1q)A6P??>Ktr.c0BMtr*IYW?`1PEZif2L%-5,GQ,ZM[`a%]*N9J9/l,J + RQ^6;7)UhcDmCoJEB2&#CY]^XLsD+?*Wa$jpe>?TPlpK&X829bkt9_o_+q^QCYcdQC=h0I? + FHI*%4clIO2.F?F&ZbT9=[d44N,m[U1#raCA[mZ2-AIR46%C_!#2k6"4V%Hh;&i\SY\+Gs< + jV?G@(:Z1;3(hZQ,1Su]PW50QdFFQ1jd3L5R*lG3TUhOS,U;!cPep]Pg%?]G".6r`RS(8fd + ]K:m]:M@gGN\9hc$leEb:H)pFW8a"kQX]`cqM)_p + -S1He+Uc+DS4j]Yp^>od2uX^3g]@FbP>7Qo>R/_AQ8j]5+021En@J%!mX + cu"?)Q%H'3EORc_[n=EfC;DiMGf^&6/8^9GK7(/hPdgM6f]pdB,D4+%KioE6fe",PKcA]F* + 2"#eXh5`Qqi=&YRSkH$;DXM77@hk)&lN3&\U0`:X.$3QUFuG73UEq1!qf`;p:;cM908uR`N + (ZHW&gHY3jSolo)uYZY=m'CgEesd2*F#a5$M;W,UG]1'LL:o]#7=Se4mR)>JAU(^/TDWgq_E;;?Rt]_kbm'\HZm4]GRtCH(&73CVKF]t@]Ph:#Bke33c + \?1UlVdo#2ViT0'>jRdMmUKCi';I46>=#4,HK3kQX + lGq&FhSj`>^04&?Y%>9J.jaaW>.!0MJLcahaJFQ%4ntTaAO+e%Ap+$CQ>")TN+T3mT,)6/A + WUNd"Qo[effP#+]8A2;F34R>k>dp&%LgKPj'^soY',.aQ([7%Mp<9JjU!GkX + HL8VM2ofXQY*T,:877SZq=d,A>@J>=5bbo`Lf/)J\<.=PV1aC0j'gD9tGI?9Q1iA/sU9%RXN+7-`Rj1j6'';bLY + eZ,X5XW=DSe9^_4ODrf\nCOG^(m.=itojo+djXbX+(0")$^1CmA:Wm4^b@GE(mU`MI[Y+Z> + T#>3ro$C5:?dd!]jo819!)ZJl/FLnn2&G1=RUB*aX + _%db`n"OXFA_Xi63E;9Q?Q`eS2i\W\Q7Ko#(*n8I+"%'_QMeM7MX@s(=M=]Y@cg'SM=QT1? + #hCDIspXQWgEcDF=r3bj(Jn33i4-""@h@_NogjArE + hMrhs"kA[#t5+`3$5"eZ.%:02+h@c.Dqs\BTDfI(B:M&Z;rtNe>]k(4/EH])uIi2SD=n/Z3 + :_t,5c11>d`E3?f?VY$M*lq]2$=AJphibeA[jG4:&(mJu?f#KUenS&GCI(_Bf`*O+%2e6ue + FNPWD,po)2_o5mQub`?@)k.lV'GS\%qi(+D:Ta8:DVZ#d43k\9MeW)bi-Xkg1@dk\kE&?Nm7:1"8Al]F + T:1"63LBr'og%MN4E2RMQL9jpE:Y)?1^>CeY'!?Q[X-+NTS`fKXsi9ab'0b$U`O[shr9p1M2Gc.Kc[fZb+4h + U#XOCq)*7d=uP2O"XWp0PsOKm9H7MD'&(o1XZLbau7r+^=r1$STBI^mEI#(NM078U2BT<#Q + c7Os$T-1YOjpd%0Jr4d#^((63`$mTO;F=)?)Do5=!Q!1ZSu="$K3uB:'Rr#'YWhV6DbW%-[ + jS^s5b4N8b%\JXbS0kS^g]"#`EhE&1H%dn51[+D,j9RKd>SR4>Es*"9dg0YkmIkS\gR?@eG + NA]Pa,dfV0eb:k]i%'O]&Q(V\j),^Z;JX!@B15lX8""1n+fOu-\##'T2i+/p:%$Lc=5fiFM + 1^o;2JWV'c3#N&-6.*Kh&2F(kI01P+a-u3/mKP_'NaQ@V]F8]\)aMEI0r,4j_L[Z=@i-'"" + u#)R`R$>U$sJcTEY8S)+D8h$Hm!(7Q@YXK)TdW.#%A]e)?sUmAdsKGQj=RYJX=FqHY+C3iW`\]A^UcfTOl*FJX5L;Ck7nN)/ + 4%4AXWg++D$E7=C!kPAG5VC24P + J#9.?DL\QGEd"%Pl5BSp+6kE+6puIXe4nCribjm$HBBQlWO4p+ + 7!Y?\"DuCrjhX$Wp[O'C\dtE+7'=5_4U'CrknE19,>OQ-i5$9Z=j%aJ$S8714QWYo/b4)/L + g\I+72)fe=Z,2rmm_/J"G<$D6%],[g2eRJ%)?QIXejTqhOZ#Xuhg/TC-.ekF_1!rop'CYMS + TW0&*u,TC2g[nXo8!rpucFV>m-ng3_/&ocNADJ"L`[IRj52+6W>&TC`28cmf>DkTGAJZ2TA + Gq@=\37Lu1L.YZ6l*?XKaJ"cE3Le%:d+6]",U3':jdjUfc?j8rf>+r + 5p,>$,G\;!P#>un8V?shO7]/2d[:&,U\u^^KX-ollEAGu.[A`$=6g$!O7e;nei<\4,Q227op[S"HI5o3jJJ@Y+bj,8,DZrlbn:V1&ejug@nr)m=12ir'/g>s'T@8JrfU + 85M/Zr@;IfVYqc6rL\hf.ls,%FOoEA@q]2<1I?:`m:-[Dcs(*GRQ&kWB5M:GnAh;2qZZ^LV + g!Kb3U]$gGp4>TO4%3iM-)J!"RRIJW(*DUGWO4(Sef*"BCX%*g&+W[aa^\jtD4S&B[bBKjG + s!20pj\M4lJ3DsH>ef=^D,Z+>0D240'1g6+6uIIX[2roVA-k6ZQmhEVKC@o + [T[WE$*cO/"9+ol$A@6)[7)MJ0u&eGPuB/T:iO\uWKk?9j2_+#X&X2D3G0pG97_2+j8QAcM + UboO1T%;:&B$X"s'7._G]p7@]pW@aB-%'QYVm!.]P!45%8j2b%n(p"lQKf1]@X?cpu!,^`' + AnCrg^&HHq#);u,!YK9`OPXh$pff=K92 + <@+887hh-7&i+[t*Q8=CBVqnIAo*&iWHR2TD1I=!.Ym_08Wq-Qe^'B4H98kDl27A9a2n.#P + Z&VC04AC-ej=T?4)3s\=nj+Pn0Hr(FEJ)QnVLL_*N`!SLq.7kFBnMVf7;GKgCeO6C4i;^f! + di2eR7k!o][T?A2J)h#lOYSc"Q?acf/EqCDnodjP=]/\P4Kc/['[.ZpF"_,3V)5Lsn6ScAD + NF_ERe(@US9_r&0P2%]L]2JC)jS%mak]MIha`LCa>*Ui`2nU9muq1^ + uB6FR)KHqM(-?$;T*Y3?H^&DcP6j_bD'kZ6ic&[VKW(go:a(f;0Enll)&FC28I%5Mp"]]pD + aSNE^F<^An_AF)A"[Va@L57]im-gZMVm.ctn(mWNHo16'e-NS(H?KWp5fhIBC=VjcK.)L?0 + CHEI*(NW=FO"LCV>ZcC;K,G="5)RRQI[7BMict_R&-CT)oeYmLtWKs#C,aC`(UC?OYgkUqNY7!QDDMS8G>M/2C5Y*qrmSr1gf^Wf^VeaRM1Z$#oH/R[;6mQjg#0O#EC:3m26-bNY\lL"u=BZLnXQS*\dt04&$^@\$ + ;,cDfaJ.HL4m/GG"ppWWi6/I\:jMIfSDq(3S\%4%ZB/K,.oUJIb#O/eub5l3$'C+<.$NDOC + B<7R&JVFJM$dE:$PeCLa>rG$e!C53hSHuEkPrb@a<8tL!]O=O%ifoPp/-R>aj.,&.cg9%OY + Wm5]YeoZna0MT@<:`QX9A?Ra4h'[=#D;,-X"F+\k,>IBID=$-k5dBLNZ>rFlkIBGcV;I + &fOZNYHoE)$VTfIZ%59] + i-9k3d9tX2'IJaX[jOLQK%SW.]t_N/8+a^1`nJo#g4H")4YRB&ORD,rE=BeM+N8C7mn#5%OW;AT5/uhRn:bQFcOgfVJsG\[Q7j: + ^%jLRa5fr8>b%ZJ!7C_rM4V]NZRI%gKO%Pl0:A$>Pbt5^qdq\R_d.U7L(MrN2/GD*LG+UF6 + p[O'dM?M]KDDBfBZ/#&e+j3&HOX$FYXf:7q + !S1+X/TcG7Z1+c.LkC1>YAm"-P_JdQVtT6Ng:W"GaAfb:qjL$RK[%3RD%Xn6g]CAHm0h#-r + &pEcAI#"Xe87-a8"HkSGWtJq?mW#dTEqnrSKEPe$!H.Bo?ln=_<[="8!sL/P,\o!&QNFUN6 + "[a;\NYe]JdPq6mr/?m?U:sct;U?[oqlK#no'&sI"k)GPi%aseV#jDjn/F;fT0CIOk;$AXn + #r&>!0_#`00bfeu(rckD1%?DB0dSSh9eretQ*W1Y1Gohe30r#Uabb@$0h!p=8MmE+2!hT;Z + W.`]=V[p:($s0;%p(ZM>[S.^2\C*i8`)IhHT0?$T(.EZ%t+..N;/-)))INqC*,I@SN6563[ + ujFC,m30NB6>ke."neD+l18]c6FV*%&KADoj!&b\[m;Vep8%1"t62M#J"Gr_qt3O6kNYYob + Xl+*751n,D$-fcX)r+I!5f:_;BV!?lD"+g`6EYTbi!-R+X&,0,<\&2PP-2L-u;Z#/pd;FKE + ;FTf!E"S,B:E`E9`=Tt_T"m\fMmNDdp-9P1E#82)J%Q=Wq4?h2E#Vq*)/klVs;F+3E#u[*] + :1FUuBLC4E$?E+uTh + ::[$H9mp6\!?;pRNYPQrc!FHNE?W7@`a7##6Bi[SoZ(=%;U6#AuOLemh5$B1NM(#`_\/p2`duG + =aDo$*Ihh(5#3pLItQYDPr/.:>!1r5;G?:epBi2X=35mnk_9&aofW.%o[Bq'JD + *rQQ&n*+&n:c'i.6A=";G!@rj;+(:@XneRrt7Blm?pFG]=*bl8N(<;9=O=BAj3QX]bfRea: + `G1E")'qEa>G8g4'e5*I$W`<71>5U7feaL[De\aNb`n'h+4giRQp!Tf+JCnl,fZL$s.]V0= + SKH+NX;1<-D62JI%blls?n10k/]f1GcX?Am@5KIiZnGk;13iFLYu!YPmIpa,0gq36Hfk%h;:9=X-pG:N$/Bb + [gd<4(kP^C3#oLYk`,gf+8YnD:JT'%d._0$#Y + SKDTlGUNrLjF"NOR@a0-!C;_mmEO`8O1nkUbm:GBrC*6TuroFk9tG:?Nn/QIj1@ + ERLbOSQsk^#;;F*$"'*OI0WQ8E#W2QO+%t3IT@6?cnO<,&"6BYZ + 'hY%b:.rS:9unb74U`q11=_(bCQS_SIfjR\_2S5':nWHB"@)WW&($lQY3l%KW%hAYc2u/kd"9QGgXD3A52%U\+8tpn9C#8W%]R`L9j_[e$ + ?j\s/Xp'%[5_fZKMKEte9]VmtHJRXl1i,H.!qW;,h&%ojB`>0Zo92_=3n=%:N($TLi_OVND + o3k[OtAZT#[U>!d/R4b92doq#WGUBJTn\D!s76I%'Ph2sX77h_q8_?_KH[UTIWJ-MAL!.pqRu + 9qjT"[:KC.P%lZI6M!+\c19U6pX0%_W@T_n,W*UUJP:0`:=-];0\H:bLuDmdIf5YGI9VV:? + H\&LY7n!K:#-N0Q&Tba#n%ePj!2<#FE6)t/ee9obEANmhC[q"%M`"-:$eL>LX][SEGrltX4'PrSXu3hoEQtYme#rUC4e,@iqY+9> + X$2lR552@_Rg$fSlTb(2Yh-j\Xfn)`&"#$cZ*jnY2@Di3P-:C86JF)@fqZZ92.E)JReQj)> + LD@=n=5jE]iXH0GVSDeGU!B@NDa)gR,4r9$*A9" + G.\*;>1-rJ#+HL:"pSr<^#_%8@e6U0)uoH`([#+,JW0dTa%R4YaDqK2:SGuNFF'YEaQ*H,su;5h^\f9KRTq"LcDK>;[ufXe&DSWU` + GIG(#'X1b.Aq&``7,?\.rdl]-Af6LOo6X#\a>22-?gc[c'jcl*s`-UfUS54GK>K'eAhA+d/ + e_dSI[?f(*QP9=a:]mrppW$;25o'F)QdqI_`L,-uR0RTM[M?m\.q-%C,C;46]/Rj]6-9g3Z + h?>b4KDuIY9geR7G^+:e7E21WmJN9Au=f_qO!cc#G:aZYX_*EQ1i(`DKFp-Tr^mf9ie-WG_Cd=H^8/#/0eU]S + #9fhVIV#k>GRLgfoRKUoe1LNnt'.sF1\A4Ld-:A0$!b5Q09NVr@"N,dETM-=V$%W.b^mECo + mQ(K`JM,kRW"S4q\BMaSn0PFJCB@[QTM%6Waonu#/2_AmHjO'[MN?JI[JIk(+"5&E=_&6\5G"TL3`&2TQ.8fRXKF&Cc7?1.h[++@A0 + m1E]%7">WT*&B9808.`Xh$khh.0;(Z&">6?6iWLjX#S=@o$RcRjJI;"+"=kT]WWXdo6CJ$+ + :P]RVJI2YoSq.3t.Kh)k6=L'DTL)WuJI*_9OF[]$&<)/G[)rLq+@6tL+Ws)\7"GUL&:T08a + :NZK#F0$,*M>^H"<=(#`GC)0kR_Q]#,i)HJHg9)";r=JM?Fb=6%+uK"iq6YJH^i@=)"U8Ri?J"o&^2NFQs)?,a?kpcUqTBSrH + Q(R;Y`0[>MRVmVPa80h+Ve>jPJ#DK+K=Rpq.ZU?tl;@PXTB^_$W1W@Cri5IF]!AH=kPDi@X + =*kaJ#oREccN:>>2$HgBDU\=Y,D-aJ$16rpX-1FqZ=UVQ#"Fm( + !rdeIW#=0ai#,)Y.KEq>e+6Z[RTlL'd@4E3`W-o5O;>uCWqBmBKUjo,Drek3b01IA7)P`Go + NX,DB+6`?HU[e>F@Oa`9W?!:GoTVSgB`CZar@!a!VE`M1<_DUf/Z&":Q\YEG+6e`6VG[>\a + 80M/l)ck@o]ABeOT/(NrB$)4nitUUs%foY5H#;$Tu["a+6k,$W3Q>s8,A:.l:":D+6pdoX"jUU8,B]ZlK)3#QtO7fqGf'*-fGOWrU]#hjV@pbuCCqZT5_$$nrl%D)(E`!SJ$HQXa&r3^A>B*d&,&a_fgu-SA,V_? + VI%JLRlI,Nfpsq8rm-As5TjnLKFdVf5KI>)B[&!'2!1I\RZSR3jEQ0;#-cY3gKeU^KsA'A? + pDbps!E`%Zo5qZg`)H!DM8XOP[XPh)q0(NXo6&F2[\M9_#GoPr2r#g']Y^9P&cRZ[1Di\5K + e$G/e2aC3TiZkSKjp`_u.:\o)q;%,HGr^\_&tkB^H4is"]6)b7F1umbQ@+GRk,?4@;e,bs; + :d:FYA;f:MV@S(2h^k^T)lFD[$mc8s!jo,^j2s%l)]6:^hL+J^9;OV/g/&+q>8e?JsO&jAfW.4 + HR=`0aNArDX=#<'SoDT9I]\;G(Y#.M^>1=+gHY&+tK(j`IeoP1j/6Kq(BYJH&eNqHhA`LG< + C'adFZ8J$)*:\Yac"RT!+"9o689Qa$Ks"9!XC$,8I_/E`Q[Y'$tBrkf!<%\>JnH;A8F5,[K + >A&J8,&,&FWW!rp'oHo:=[U;>QP`KF@7Yjo"HI(:W#,?9`O0jY(8;NrT,>Cj]dY_$t)Tu9[ + jIhbNk/]dOXZ*.0WO5[AGYa21FW^8`U/YauhWd[/.RdLX&,MntC7C0%Put=-LFPORh-I)SL + !D^$aBf87V'#B-[FX'glQ'PW0nr!JO8A3oOn>mmh#.Rh_>J)2]tH*9V"LY&XM,Ue[;mtL>HOZ"QDFJ+k + ]ZreFEpAa%f]bdmsA5(+F1lqPLK.qCcNVGq)-`$\^L]/+-3,UuP$p::!KS^fGNtUH";^9TT*]N:F&0PS/Th.n+G-8,b`o3=d=W$"8u46Hkc@s!Hh)UG(a.<*N"8E(lu*QEoXZR + M/%BY3"TT-J8@Bq56*q)8hr2'*M_WY^bh0m@G\io$;,TN%rHN5P,G_-UTMb$%?SnYcg;,VS<9%q"X,hf6%CLR*g:t&M@NgaTeF)7doZTuur"g@W8BTctGM07"nIRO,OJ- + TYul3dUcLdf:TPWtQ/J,:flM8i9sLEfZ,B2G)H+3u+!k,7-#4_ne&Nc8a>.41Z@ZDq\SOCN@m&KsVIM6UF3te6dO)TT1GW + jS;K'6_J3A4#4c;=uF$$:Pg_cgB-d1@'!e7R$JA)nc4tG[#@YZdmk*a+Z.'#uhb\=ecNuBf + LLm(W'HVhH9Pa\nD`rf;0o2W26nu8n#WRQ$tTsqG9>X*8&S&1r@P@'g^@(%$OWe&*gUj][K + aL5UYWo;/P!R3Nq]/I!35XMk!6FRt6Di@V6[i>[S_;$R"h)A0V2e.%sd_d+m`CTTHSqDC!b + RpqEm&[j`\,l*/'7rMZclZ068V;_hPD6Sp)Qd`PXc/d[Z2qJR*j*Qg/a+nP!UqaT[ + Mqpj->_$QFVAjY,W3haqd"B'KO(%6Gi8ddds0mTTS9.\h#P=MBspWZFXKo3ch5e;Z@rq!D@ + L7UDp;Ggc;TB2DBNj]+R.d,I9bt$mY'J9EV`!/%s8,PgR.\@Q`,b77/Z(CG3qBdAEtm]f]E'6>nYjg:_NJ4bMEV + dlOCR4O*KQVn:culs[76J2B+C^*84o1hF%7LW4@k5(%%XN+oZ8U.2KT7_tq@dZV!G;F^O^M + 'MN1)tCrtZH#&ua%Q&oC\=\(>IDt*[HOJPS9hrOH^m3G^W_,@fuLkmXIQYJ^^Pe0*.j^YK< + %#]85oa`A-PSEf>rQTS[=6hS;P+`Mk0d)X;U%hj:4`Q%j#g(/:2b'glj\diB]ks5ua-l7$g + 2]QpH;u6'Ba-F,7rUM@WZ,8`Up87%_dqk/M6#RB1'MZA]&GlDWb.[,?a0bO!7'm@'hh54ieY!NnX"R5JJUa$Wu9_t?N0p.',77t^ + S,PPH0hqC5u`2Cs875d3K6ImR(>gF"%Yi`2V\JEnAIkF#KVl1[knFN8BscE$qSn + X2iJ6+F:ZJ%&BPgUaXe(H4;4=a'Wg>G\29P^8F&R#RDGD^T?Ne@]UZg\!5[CD;;7*t"-8N8 + U-^-#;BqCB99g'QVS[/IdVG7;PF@UjX$X1p;R;t3gRo/.YJU4Adefh-+XTQ5ZpR6h,0[J_A + @FpH0Z/MF]$mh*Z`k+=4A40e9B&+XoKQ"P5U7=80b$B0S3son:/"n]b\]b[oLM(N=A5M_9X + RGS9ME*-@SH,`bl(8KUeqo7;GAuH9gWF9<)lY'<[O><1F(@39\MXFI_:/ke*/*/s*kLNUH$f2Nk>slE`C\D+IlKX,V_3UN&4DMDml:fP85] + CNrD9H.\YufW>^obn8UY&t\J(XP=Q<>D'b?P#6ps;3Qd_V3TH5s3\^;\AOYB\ + MU+m9'9@Jkdbc%b*/8HjeFI*S@R^_\bbu&Eq['hUWXmY>,R=:s4'ugNb>UMC`i]=MoSR2%s*Z' + Nk6a=Uc/")>@8`e3Tpq\AXWZ<1+h9-K\O<:Im3Pp6-96`-3Lm*Q/L + fC:._4=c\b*&YD(]"?001pE3_K9QWr0b/XLao[dp9.UD6Q(WYu?pn,DEHXS3>V*[);i+>CA + _?Mn$\U\kGi1<&M + ;,6e#cN1.Yb]5/(:0.dnhJ@A8"mAa6Uh)r@.W/8\!oMmq1K2sSUp"I6(V!SJ+6k3SNg)jE_B[j`"Y)^u[%cU + 1!>F$W#3_87`7U+FnBmNV8]Lprp/a9iKiq-#O<\\=f1?o=i]^/V2"#+5$fJ>->>iRa-I,)& + oWE$8fA)uT)r]f4S6gF=*h4ClW[RK#hNEtDJjHd40rG+80m^4rH?7nrJ.p"qk=R3t'??L=s + bP/F,^L!d:ca.(F_di$[=5N7ThYdH3$j(6NJ*#2`jlH\Xo""+Q@8N8!:p+:Z%a8Nb%H_2^k + &c_:L1ns]d:TN]d8PY5CP6Oo"6T9@3.r51T%@U%#WU*9;=4cX(>@H]GLA*gdRJMbL>HRV-Y + L[PkM#BB(4XcHJC<#"Y@cj/4YUPCZY3UNYMP%5:?cGX*RfA<6_gjQ`ZXL$[(AFa]2nB7%OF + 2k-<(a.6r*=-cB_F1QW.ji1#DV?pTM7L!C)p4@k+gsZC[iP1hSbKq.b + Lh:kfSpBu]#O<+&T%_dhu>4o-'30(!p8Z!D,hJF(3*`+1@9g;dH5PurcP@ZgN.M.;E^Ugep + :`qQf/)qi=mbZ%A\(,V,GR8DDm)TA"qq)0]@U2F?Q\s:?U?^kf\G>drt$KKfJ^h]Pc!$DVn + +A?VJZRn'h9!<">*k-Bp6&o5u@me7HA_h<@nQD\mJ!`c..lD?`<^@ + ''/`JECRW;G8=MN;t5=P=,.[TD=-l%_fspackUuq5U'@O[H*.SRkN"-3p3'baX1NX&[%Y:)lB"/_Y#&0"2bn)Zn%*\ + l0^s%ri)3+o1JXBhW$![nR""up_YVP8J[l/f'TOjK.@Koo+^rk"ZX:2'1.DX)*W#8&/:k=h + gAEoZnRfb!&a`'"8EX#*dOs":[)&`]JZbXYD1)p^V"!HAAA-a%O#!RU!?t7C8%!Mdo5fYQ5 + `Y&J7JW6=4=;]Hj1WR;N:bdn;@07>.+CaOO[K\:6MY&SW<")\-'L9V+0fH+D(BR?Q`qI14bdJ)0URt8 + /ZH!QPC\K>G,Q5#$R[2V-8G9BGk?3Q7*VZ>9IL'#$2pYC'I]]AR0n=Pt2cjJX$KY:LtY[1' + UXLAKh=UTOek)"j4oN+71NVe"?#!rmdY.Djo:d/XpX-[`A8cJ%'(fH@NEeqh+ + >sDC^b/TC,SUk+D'erog!BRJuLMm^odl]BB((J%R0+`dbY!qp%FP9E74g[.g1Ird9LsocNA + PJ"Ml&Ic,MM5Ni7Q5u\,A&5`BmKDc#coH=I$Oq^7(BRc_`'?g`nK0#"rfBYH6m(Y1J#%YpP)&m-MCCP6,p]QVd]!1="8se^o + XI-6nGc/oM(p>FbaO=`BclTDJ#;3(S-Sn2N[`@;-\V,aL\cE@l2Z=4+o\/?qM((]+[mfu*2 + JAdOWWjbJ#QlUV?csdOt(HI.Kr5>L\dhhlC`X,U&M(tqQ?&4_-&9WE'Mm_s&VSd?4Z^V5Lo + i7=Am7M/7jpL#PuUclSsBr!WKH]qU?VQ>%+Oe['phOs'+O^E=_``5M%Uh>m(%J9h!^HQ\+i + Olcaj^@K*tGqYD<"no:kq!5OsH#TrCQJ$>XG_[$&K+7)=>YpLmU=:*V?br]V!*;l_>[6"RrWDkiOq1ir83BNs!e-KWAmBPiS7(aENV.&&+ + [Y'b96g9c;D]oG;A=9l^]Y\r4+FUi^LFe@N+mlV'SD0gklY&+^e.*LjN^0(%N-dl9 + S9nGb#%%rcX( + mgj[T"r4f`;s54:d4bN\,&+e`\i%tdlO?*Y5"8kC1"-c&I=pi*!(5#GB+8`TatO7]=c,.q-EfN`@J#@;cSk&X(O"oWX8*L$sU_8M+l7@F`dJfp%9Sego3_ + 2gk,c=0/T-*?OJ#YLb-7%ZDPS/jN.hVc^a833/&tW+Gfj?:AS?l=mrF:o]P:gFb]U.(K@8' + 7K[R^2K+7")]9UVA+k)@cXZW210p"hpbN8'h#H>lV.aZ114s'R)MJWKL43p0K1SeK7m:IZZ + qfVngZj8Fq_\b/1c"Tk?]&X/:L;Eri:s(/PiKi%%X7&ZprB4Ln3O7n#eg'J!,m1lD'QBot` + 'dTU"8Z>UN4@fc'>p&^QR+!t5f]fXr-A:?e&,LQIWWR?\PmjE%JsbH9rCNu228Zk,TN6sAN + Z[ppX4U[WaUM#XsY"@!FU"D3WjbX*EGRe0p + bOV0<^7nG-f.?FAhI;cc$M`9lPV:-F0#ru5I,FXgrt4]eZ^@EUt:ORb"oQW!,q/JY.jj + s^8P)+uGQ\X/T5.d()Y=F]Gf^R/Wogrt]r![&7 + cXT=6Cq;)RZdfC^<&eAhg!MT3`j + r8Pn1J;\5oc8n/753+5AJnp&#j:&Q>1q=!YjetSXkXai(3p)6%k0'u`CUf,%H:BN9LX(LaW + //,8N9LJ&kt%E1Mr]brm#(5.9.m6'%^*S1beRo=lbqND%`H1slG;]P_Jo;;8H/"816ARuOR + ,=GY3M/;m_n?>`G$sDnJq$)54D6\PcSnimh/]?(7#ILa@U3ha`-g)T(:!To*h4Xm&$1(5$W + "mRSQDrM6P21[.QlUooEA&$-pePd`rGHp;u=g%.'SPoUd]5(iP#!08@i2%abUrd?0glMB_l + @,bjZWUFJ1NC-$&955Qg!C$JJ5X\]dk^_LQ6oK:qXI)MS4&KkpW"bDOV + (#k_dRR/T.MFcE-=Ci4pbd^p<1ikhbY[nA2'i_C4p@,m*IOgCPnoON]nN%d6fnm-bN#`@3@ + WWA*f&;Xc=E&o;HMb]bi2'U?u]n^0>;M/LEK5k:n'o'\@KXJ2*kX#2OqRL_gYO^-)OZHQ6X + okD;9Z#%ZVd*9\^p8/W!mrK<3n=L-aT20uImtCIuOO2,]d7P,KAnLkBT,K:0UCAoP;64HWY + 1)O&)fN2)YI\JP^86feeWJ#)i`uEOdetoOD=Q?YZ/5HAmf"\pfBn85m>o,hoEEUiu,dK + '=\"EP1]X=$4Tn.-Zr'-V!c+faCAnf\&I,m$smG7lI1VgfpF1F$isGjmc!/aD4+7c.]q"L: + f+nksij#W@=657*L + D$.>LS#ZD('XQIfL.h6emn?jd.\LYaV/.fWK:rJ30aM&.Ol13?h7'@&_h$qfjN3_OT;X"[I + gCDFANfs&G;Y^idr/\/a_[-W'E-aCPHuOSM+KJ-.l-WIQ'!iC% + iY!P54W9S'Ji5nF%VhK:DED!'e,[gCp`hQ9f=Y&?En-$Z"ffXE>K:2(K5RLo6ei_m5QhQ3, + s^5WW+sF#,'G1),lq@PFS?teV*esb;cg;ebn8T!2WcRsgYQ!5WeKAf9+LDL1&.mT*r?77?+k.LeE$F) + T,pKQD,4mMDcnmS@3?um6,R:"g:dlXk8:(nAZH`I.E`h8;R0D#I"qkHq;J?>uD$J&R#7Fr! + c8-in3'NFT-r>Ug/kG'OO%@W_8MZarn=E!dUFL5\.]?o.n?4>rZA28b]P`>bP/?GDLCA4H% + 'n09P1'jqf*uE(%BK?jm^4Zb\kR/00(A=)NlqrNcqj64&+]m!Y2KqPk#-74&JGmUcM%pRr) + E84&i1n4mgToKEt]3E'2plVZTSuN5njWP'MOte%lZ?V4BsU@23D$!afdT7@5^tnS+119ojI + JJEqY5$2mI]golBehK_OUj373^G'nlAPQMF![3Ur_&24")9W7+b?3t\^o'=QCd\Q\U@>;O4 + J;or7mb\WKiV[q<@NH"jHd69G<5&Gj7[GO12%6l>g09%RHCgtV58 + +t^s@2AE?SeD7DehA.8%Q6eoCm6h^C6q=@IM^nrF:uIFB91.(,]69Q!JIW?P`*A2g7bmf1T0UFVkX + 931!]JpKL`QM]"113O`X((Md/LTc-_)3nJQ(Z*SZIWhgH7V/O7UNEZ7dcWi9T>]"7]Ze\mK + lWkutrcQrIDson*jZcc/fl-`?dK'gb9f)OsaZp.:IHl + O./P&Hh_Vh_7PgFu;[r38c\X792[$p-c8a@qBo[m]e@TH$PA3bJ%+_NF&CjJJ[nScllP@60f#!Di`ka + :Em#q)/lmZdf0("\V0TP54E>V<4=;Umn$M<1KA/$+cAur=F+j;3s)j;(phu-PDj&MWB[RE]qD5)H + MDk=_HTOhdqb9SE=,3!)[af<9e]MZQER^mfSQsIIJgs\*'1Rm@#p]hXEkh9-o)9:pTiuA*Z@Knm\to`Ct.VmAq9g4=8QXc;pUbBW, + K9ZO,rD0,QIO,;VkEIDs*YE4VEdOZ + Y.6uHPLe=2f[i1YHaS#_?+2!eC+:>. + iA]=DdgR8;J+:cP,dS&^DO%uSmPM[X1134Vp(]e^MA1Mh]&FYgaVc_o;Wo2/3Q]CVtgiH!7 + ;(%5#7q!G&a?)dL.W\J_5f1h>aFc^`mOKAW$\J&(j(Kf?/dJPAT249VMT>LCUGM:Y1RbJe: + 5?hA[Y#W*:OjC2ZeZTri4hOc9)YmZbrPeHc`\I(h;tlTAqj,_4QO:L44F(;`&RZOsQoEIK( + Y:tigB)^N@@WTke2;]U&'=H.Zam,d]W&7r43JHGS(tK[oO5r=aaou$[C0AbfHm*$e$aKS86 + @[#=m8Ktj%4Of.Qm$1I.+Q\NOVlo\0h#0m:!A&S_C7CX<[]e'Kft%mJ\m61J]cA`;X_b + M(::Q9Z(m)Vh]@/[q:_r*(32<;[nYK4tV3)k5(AlVTs&*$6a5'lrqrBg^a0'\RH!F"FouY03Tp7`CYI!K5).._QB9.+ALLAUV@ + 2gS^2rNM9j[1ujGeBSVI%dm4/[NkuZkQ6A>d$RYZR+@#A=HH>ekaL&DmbgJ[G8qq>gFf;AS + /YXB)"30*8fldQ+'_\KKSM.]^8qaom!@*[!st&_m$]3hSj7"?E,BOp!b[a:R9\%HnAYJIS + i0"?%Ai=p-]*6VK1b%1!NjJIKU2b(0t`)?_df6P9MUTL/iaJICZQ]R^Hh&@d9!EOl9)+@=3 + R/0I9lJ:S'K&?:9gL_-G7$;t%1.%inY"=Kj/FJs`sW">>I$"sm8/tbO'"=,*V3WpZM67gK7 + #`'%HJI"dXK/fMj!X'U<61P."TL%Z6JHoj"FFaZF&91%06+R1<+@3"1)B_>:+Fs_f&7U1p# + S:?:TL!T$(8*s&";TEW&6+2a-kK4+"R$Ck@]*e=@5&/p*#L_(AETKlBX"JA"H"9[ + LO&.F*iW"95c!-d-"!?aW9cm#+W-4>H6_#aOb+@k]"INV/;+FtV*&b:Jp#SHNaTLZ9jHD!d + &"F6Y1Q:QQJ-kYBg*U"*kQCp`_"EknX=p/sj7o"#S*=*8&JK2``JqKaF=rN3hk^i_iTBN9R + MkB4YTT,_FqEg@;kop%iTBSrHQ(R;Y^mCoIRVmVPa80h+Ve>jPJ#DK+K=Ijp.ZU?tl;@PXT + B^_$W1W@Cri5H[]!AH=kPDi@X=*kaJ#oREc@;rYRb"pLl^@a_+7#p*]:\E2rk7qslMeT'Cc + VL4+7)Su`LlL2rl=_+M^HTPm+MB_+7/7kc_'S2rmCL8.o+U%D0c-4+74pafq7Z2rnI9DbBD + aP-i8FD\7b\rJ%4\=P(1#l)k,s$/uej8+7?]=m%<_!rpHEHs6/sbD_'7c]a+GcJ"G'eHcPs + P+6UoNT2Y0,:[%gCKDbb1oE%8()#ljrr<'lH&q\JF&Kg4%J"\UrKh(tU+6[;LTsQk9d0_0A + k`C:gEW1hcqC1sLU&Lr[qO\kpKR^!^/u_i3WE!]GJ#_K,X@N,n+6q@*X*I + ICT=U&MY + oqX5Nl`.+sNY.[oJq-ToqILl,N5M-BV?u-m*0Lc\la86;flo]d&);`d9jt*1[^t)F$Xk@ar0\)dB\D,rC2Js@\2Pbq;$Znd+6=ZY2g4oP>PitjD@ + ETWo!CX(j,%ac0ul9Z,ip#>C`hcKJ!UBsk$75EF=rR#Qf7pO#Pirnjq6-)QWi_2MmIS=8:R + $nragl\R!272U-15:nE)^>C_N/.6d+6_E"_oRB?U*N3aaUirt@K)Dpq + FZUL1+5R>W>2/5=`cTitdr>=^&[jSf#j!'O[!7W=!.Upjrh1S/I? + 62o6HFEc,#k68;T&H,8GO8OUnj(*DmPHV[f953M;4TJQh:&,&sefjtD]e1-2Ll6/QoP<9rRpj]XP + #10TDj+7u.hd^iW5>0S`I7(KQ5qJ/gib@k`,n=Cm< + !nagE.e.feJb=ES5iT@Kp]8.;?']IojDiYDR#O[pur%^BWY:?[lV!">YJRfG(]e+=,QZX*S + \30k((lB62t@E22JM5/`kX6BPnlas0>['b<;c2Akk)%3KKDSg&"-MhcoERa(Xl^#OF\BAZi + HPnbu`KgjBX.#9W[%h:pR$")\/A5_pTm&,V?,/lWmEf/3pZW(oSFQ.3Cs@M_As[Yg2h2]qd + GnQnl5&#&[\8'))_O8Ia\09u0dd5q0$,cqg%rVXF_!\VII#+6,[C5i>q"Q6p3)l%Qf:C68M + 8!3h*Yf7TBnc')t`>k\L;#eIYQP7II+c;NdUm!&'(ZQKJNF`6c`sU4!8s0.7EW/8FEl(phA + WX5EaT'A-2^d])JM8jRgftL8.W5_o>od5in5:lQI>)7)AuA,f/8D(n8S_aL8Z)N]Aq6IW=_->JdEX&AE&WuC + ]7VN$b/2Jm2([C^A/2$LS@g2q#d=ZHAcVJ;tk*T*p7PnaijpoXJ;q + 3-"aRK%6L1f:UNMO(4-A)i^u:"KgYe\Ieg%+pp=cih4`\T];?"UR/NKkb1W6Tj$g"S"-1AS + Z_f/Y8SFb4>&Z=+hBH&U.Y3T>5H,eVkTZZYM(P2qCT:p0fm`<3o^>f;[+IVZ'2N"UT:`*W] + dT/5.bf'4+]g]P7]'M'-qY3Pk2T#lPCi$)'nMe7#SpY/&SXOjtrNa[(;0O'2=oFNuC)V#bt6S,OR!HWi9F-[9/YNCKJ8#Ri/-%"^CJco0)N.nS3jC"0Tt`A\`V^l#ap] + "OZXFq!td2hVVk.H@L$jL;YP2p6Wc)TeS^bK2hs*rCOK1_qYDoIV>U3,/.IH\fMF-KPQpQ,`Ti13MA"4,uSN5a^0+&^Q.NUVg*]b + gH_[?.4;Yd%sV\4W\3g;KTl7F[u[lVYd3npgTRft$@#>;\7,Fc#$6p<<94ttd-4*j4TMn-b + V&gV6,9M<&EX1gu;(aS,[,kJkfPJ]LN9/6!<1(mYNH"/spai+''-0$C"o?A'"c91_u1=Bbi + nn1*7GND_ujh>PE1 + ocHjbj%ZulGgI%`lY)Ig6.npDa*f456PsEjDl];Z,\rta%Q&o>P4um>d`X;[J6U`S9hrOH^ + lX7^W_,@fuCelXLI_:mX'gQ+r7OH9#Wat+a + Z3Xj/),g'@GJ4q$@F;ra:!AkmG\:4e?uP\Emfl)<*I2"HaJe*TWcmkm2q[8< + &W)tCs"os2GgVBjF9DdJ/kqI5SSf8+jWD/ZVX# + ,^ftad7#/R*LsV'%5biI\jGZ@BNDS)L_!?=VKTO"YOjP+t5qq7`7$U&;Q6.rM6$/J[JbMHn + e@:-fahs#I<#nQk*_Go*8d$8/'HnTP+tagu3D?80@0o82Va>MQ\YLZ7\J%TB.n[c23W(J?& + KOLA0?XeX\l:FGBdZPA1eUh*3ik6'"?4M_36RaIbH*bJ]K;mI4Kt<]]2ZPWBgb>t_8I>1Ug + 1gU4N,018]4gKe4[SFK>Eh+b!`&K<0P;>bJtADcG](qeD&/8'?lc\dmZ+CLFW + -ieSE`)TmoMRMABnFkW*ErAMrm3Nl+WBs5,1lm^kl* + dOu41W7f-/aI;UKt=#GqY25P:gATCRf>u-/rc]Pn:U8>W3r&-q@=.K9AXT,__!8#a%SE0gQ + .r;ZTKspif==Ukj,BLr$FY!->X:'6]P`E5Q``T7]\'ofJ1OrT`'Nj`;3&MIS'c.s'(bJ,o; + 59"7`KjBGQVlLt;=0e!q?Kn*iBfCG\PskXm3Pp6-97;=3NT5a/LfC:._4ms\cf1iD(]"?00 + 2KU3aB!pXYSVD1V0)6]!DE@lN4thYe($";l-g![Br"a[8Yni,R(Y2r'FVO]0o(SgV#u/bJ- + Gd^s&(Y<.2P]Mo4+<5ldTe&k)]bKV?R<=R#L)pcJrd7nK$1PO1 + ]$sh-D42&kr^#daDrRFpViDpuNegc@(4%MhhA\Nh3RN6':]kK[Lh`fk3h@sq[h""M4o/20l + 24C)f)P962F1(?/c^f5TX`lZTGYck8**mk?_uJ?+Btt_j=7$)kqea,ADb+_s:j%CeK6';AF + NCkE:q_W\bBUhWH:\!l;$DkT'7N4lJ#pVL/DLun",+L=(YLAWffP#C9cI5t + 6Hof'JL385H6UO>2-PWdL=[DVsOe+L3""1cnr\l9kEe2%k%j1ifj_(iX7 + *4Hmq4'?i5&a0N%s]`cm!)_g'Z1-J"Tc(ilqm9*]m>!8\<7IAOa$U@uj6,ke5^+4:!@noTa@1Vd5%#CBog"4a?Zos#jnQX9LgR;u*bG78'oZM^Cg[0Y=Sm`eCP=>m"3I:f?r + `IDQ?5^`gZMia/cH,M1?)]dSZZROb'frGh@ZH1ImUiEkS=o,rdr#kW9?toU4.ha9s!Q5*U' + (9gB4WHUTgtONQ^&`B_Ad;jsA4r&E7guYtgQXD7sY,/0tdn`3$jiXtV/^S4*%hC$%6!%\L^ + 'a_DkV-B=iiL!=@54DJas>m@65*^@X@h:)V + %.IRBnEZ'PAH_X:'Qmt4G,baFc]5_N?0]bJ@m@kAlkSpLV0fY0!4JcCA_$?0ealU;oK[<7f + KNckQ=J:EA?6!YAo/">I1KqIa)'mQd3`YN7Vp1McK=4(m5Aok72`V")`Y"O@!6YFK9!eFOD + U;-i4Uj'dWq@?iS+UASn@*)fTQ97@pTP69 + eB3Gu8KkiFog9Kp(#mV*F`>Dp'\1V<)k.;<##l"rlRHqA6N^uF`Qir)M[$R>+p1=YGBeG_; + 1Y5;^G;WE1)@uE)1juV%943b2rZ]*]/*F)kp4Rb5WVoGc41ICBO@W6e!jDAHlFD/CL$KRc6 + cH`dG*_^?[n*>)cAE'!BQP!:%oj1gnnN?R3el:Lu(2NSenV->kQPo-9!-,_X?Pa`!&+&,1nr8UU2?,5Z.oBX$B(c=IhUG[u0ApG`o+oqTk; + -ag$M.$H=jK"ahKsQDM'u6KMrA76T4&[a$\S22<^5*FCd + FLVBG*S8FFI(oWF9b=%mN)5kO1eBhqOn-7[MG/(AGfZ-N#b7D[KU_XDY&C!4W_h+m5$;K&\ + YosMol4j*6E3GLY*l:k@F>&\Wp4n^3e1WD[[k!=/l"SQD8E,Mf=?YJqgg@h[?cF7WU3Mg9" + CNfAe:CV/VL/-S]=5H@;hGLZM20/4V][oWda.C#k[_M?[r:,_pC`Rk=0,n!=*(V^#cp*!3% + 0bB*a(?(Z[k7=#SbKq.b1M1i=H*gCT$:l4r3tM\d4P?hh^[:Q/+"@=m4=Xl4W/*402\o,8_ + 2JGIb40CR<(Kam@>EfY;T9;2bI];g'=!K'e!64hdJ-e\`SFj`du"pieR^7XoO#=$ID,@&?, + d]_$X$&+[u`6JeR'qke%Ir"=?D@E'dNU#YBU\+G=t_)@/cs_$H,h*"[f,+O)$^#U'*P5(X; + QK?=$&G6ApP#;AH_+p^,Z%A8>U+OUNOWPJd86&Z@`";fZ4K'4>c#V:Q7m'&cT&5r]G5m6[C + 28TVFJdEQO3$=+M";&BP&4$.)4Tl8R+G%TVD?q/4#'$k4`"4R)$UFiu5c.ur'7p@[JJ]Ls6 + NdY1!RO)J&/bTSlQC_V+=7@"JcYSL5Vs#["9LjhHfuRZ#Q^3Q?i5@C-^#U%^S$Y?,Ou-;l+ + nH<8'DS8"FKkcO(]ch#kW^2i/]NR&`X"=5ooGbGJXXQJib*8\0J!Zl2AF=#U.KF*69;H![* + 7B#]ZMm&dWHLLBJZM)sAHXJfCX3\I9U8g'DOg&.NjHI#[a6Ynd&RFW$-D; + (MFt6JeogYEASel\d2L_KrPD^:+.8tJegm#@Y,@/dKipIKlRH"TS6h\D#Wn]3[J#1^W$(;U%VR!GJdsaq#WNrZH3cGPK5M`^%>Z.XJdk6 + ndu5E%63oB?K/*]fTS"R\Jdc<8`.ACG+Mj?dK),a++G/ka/g,c&DLr6e+L6S,#U0J)TBJ<7 + KV.I2re@'/I[5eaBW7Gs$)T)ru_FIJVX(*+6XhsTV;@;#PnoPkX#fR,UeA. + d/QHXr=0A14?q3ns$T6Q&jldqM@i`=+6^(]UB1@QL\`hLkh7,RKQN7enj4G_"Fm45rfN!48 + 0@(G-`"\NPS0-[+6caSV1JW3L\b6tl$>"["N6]A127oqUjo8XrgS]>E$+>e2lB(1SeL&^:L + j>f&+ooee.DXVKDe/."V@Dm@ZV\s4Fa=RrhTkrQ65C"J#TCkV\J]p<.k-HO7c12eNj.76iC + $roj&u2K!JF7i.%eIriV%Q]H?G6J#ircYa-KQ=UQp5XKhc2L\f@>lUZN-8V4@9_V/4PJ:F] + krj[a[j<*]TJ$+X&\sHmo?45,0Y;-$iL\gcflf`i%ahkf[!s2kh+FgV8rkaHf$m4gqJ$B=> + `0d;8@mg3RO7kOtf^SoO"9">mp.7N;1FQg.\::g3rlbWE1*>l0J$WS_c7#/CB8lYZ;3lr'# + Z2!S%5`Qs+oXn8q%d^>7B3b=ZI[a_,Tl*7J!,B^8t-K&Cos88[^Oa^#PgD&jR@Y_k2b<%X* + 8J6mCeb#r_nUJ:`ndj_)N5k^a9WaT"b?r>q$lI + hCZs8r8Zk-q+9-5LOfV8gF9``H\L#U?#X>&V2WsoYs,E.0!!2Shij7fc-]>E)*2,4-0%**M + ]]]O)akXa;G&R8,A,cl8D#NBb_j5!RR+R$.#X:lV.`4s@i!?^Y:J$<))3[\J+SeK7l0er[H8,F*p'Ae/`g&Ftc]41i.#hS!,%4gB[[>VptjB2 + `Bhr8hiqVKDq]Hb+2KO)hY/%BWjD3s&LW">]NS3YEm&\>b($XXL4Y,\i3,`$itr6cFAAU.d + :60UU73O:s0`SIR!/sJZs&-in.a;C5m;]+8.J1=U2&$XI6^\%t&l[M_l#^jdeDE2UFXg7/L + gQVmS2*qHCeVGu%*5+83k2>A+BFD9;N:-h[fVqgWR&*#=.X)$Q\_,7$gf*nLX?G + TL-tt,h7L9/+iWA/B\jkmqoa9R:$[WJ,53@)QP;sgo=h]jr5FJsIhLIBN$J?&i&hBuj$[Rs + 8\I.)&c[LQNY@HCh[:hI#,1)hs214N&4hWiLt&Gk;fYQ]iga).AB^I]$Dgb&q+*a?[Q)<*W + `-eWejU@uN1:52l=_2WVRAN4>p0c*IP&3ppcG!BHiue*'2e + NQ\3i?#$@B;qDO8S[C1\CL83X(SM(/$pureJl^/qm-(*6d*:3MN;K8ni,rcudeXB_6V4C'L + ;%>)oF(R\*R-f"Fa]"Z?53`O'DAG2'"nE2\9$?8mjl;HrfgE"tn@Cj-JsC:fM,B->n0eCmD + Jg5a,BWkVB]lA3%:bQ*T<#`Phs%Z + \8_Y/8#rk/'[Sm0*FU8Gqk62NWk]ONuudDX*APm_oJ^`JH59`D5n/5k%H^Pg"05%h_r^(@e + JXQE5t0m#r+o.[TTVam"]*Bo^@+;Y19eoLuibXMUFmb8*lSS*sP*C!+]bcH^EQp;u1c%.^! + kqO[/)A$:KW%u/HR%+.ZYq*nihMB_l@-DKl[UJIn'W]FiD55RrAC'm`UbtMm^;#EplC[,MJ + /RTn7ACcgmVl&=L-Kl=@3cY-/OK:f'cKLCnm-bN%`CVW"diRuB4Rb!/&r^^mlut5r'UA,)'9%[3b`8##-s?!i + o+=U;.?)EKORqSRoIi]LbeBOi`g[KGdj*O"3BA3VgBa0`;#060;$XVhq0dk7 + )5&c'$:k^L%/MPX*E7Z\?[k6qFN)JaI;X"[Sm#?&T_5QHKE;)#iqN"JP__0U/o*XnS,7sG[&Mk2cP + 8b+?*td`t&hIYa:CArH&6CSB1N>==P8"5#l&=t3KK>7nXV]QTfPdt3g$C&au:7-Y8] + .$4)IC_U,;c;_Yc40eW7l'ia!QXdiEM^f'hDG6A6IVjW9WNfMCqfU9,MUpE-a>frtH.&-X2 + <#p@8O+V55/:_\#u(p32>geoEQUEK]%/HX?SGS2k9;E4i7OTWc>hQ?;nNV5:B9dL?J71KO[ + Z + #nIOE1^>P78)F(D.'LmdBa&cMCMF+Yln@15SXZd4I-d0U8n"a;$>u&O,bsRl#+T1poU?EDZ + 8G__mQZ2:YUsd:2bplSc"`2YCVS0m*,D'0")GZDD+:?:ZDDD7eT&a^b&(B5En5)E*R/W'Q-uVuI+U1AfSZM3.]V/RR32s2 + ?M"J01^_sD%q!g!FHdQCclGF>`r]9R6C&?Qc.>;u+%&PX6]2NZ'L:P08RjSM@rtK(0N?tp= + %/9Z7Eu][P%rtl:I`o67h.+@9P"i[r:&)e;Ki@D[gMZQ3q>'oEE,)?Ps4do9_4HJE`GhSPu.+8?M*i + %F&cRgQ"'FVE;!4UFB*=&Q#uatK(lAJF\N)Y(L6b*R992r=Z:*kpMX3dF'9#4q[GbnZ+AGM + Yric/4AAfQD-J"Q_*'Zp4`+s4NH$!Sd6:R\5)k*lXbRuUiBMJH5HU7Oc((ibn>n;O?]D;k1 + _jCT).7m9hF(n:ckRNA!/r[KPUqR + >)l-e)H/,*gPr9DS6/oJ282T3Fl_X*H\m.Sh&G1BBkgJ@P#:am&SCeG@?%e"BXD`&DNFWBm + n+`J/AQ$"LqmhkTd54VC4EGV*7mkls53m/>0&lW;N\R + ZN`A4o>L\.P6u(S'&Wip_3Q96#Q(L:=ZEB$6+(P*id_mn&G6KMnh]pe,;d^S#V\Bt=h/k`_ + ]:c"ag1L?Pt<0]-ZW.u3?;UCcs3\iGTC5D_;NmEl.+/4HB+'ZBiJDX:_fo-VJE[8JQ$VUIP + Eg3&@2GGh];ES?&)M3k0'>B8G[Net6EEG]dNT9"i\W_n7fg9$I*Z]NdBpp$(Z%3)B)U=*5e + G7$1UENmp751DSIGW@^-D,Y\cbBWL%Y-l<7e(^-$iK[#pYF/Z[fjR**A?)+&QIGl#&d=V"$ + ESN"5oU]/2pXe7_ZC:hZ<,eKVg^RMY?YmI(0Pos<9GRk=`2FB+gB8XoTOS5r=4\] + Z?n@@\*V[MdbMP_.smMWDBdK3@MQN`uh8@CM[!KQO$Q&?b2CKnVPnkW0/G\e,?tPA6J!9Xn + q6(i[7%TnelgLXb@?$l?M-M,*i*0A)C@/2`9,!PB8AV%jF-0c9AKc)''tDm'H[Y7J=6]r(6 + ;V_p9.EC7Ha)iZ^BNHIgPXL5fFj&K4E=*!)*XB9.1PO0%`af`P3;fdos^.6acmIbPY,qugeV1]hd$$NQ$8tSpSU4LWrG-_?@6NpZ + ibZT_8fLaYuuR(]RR3;$-7>BR2h3lPV:-]SUR)uZW8U!2t`X;WO*KA75tC"=hR9%\OK`B-N + (\k>fF+Z>IF$Q[PWQDI+(W6f-re"B7"^F(4FJ+V73h,\,>\eSF\7XLM<,Y9:0!XpgCf4Q-b + i!9_Pa%fQ3c^YG.9=UMY5 + q0oU66N3&G]SS)s87rg]]NS99^HB2'PP88NFF1m3IokSY3$'3f;pH#?V\uqZ2R<>'k_7*df + o:<`UQ,7sM=hUtO>VB8c18GmK41R0%gc_e\_csIl>PdTKGEg[FR6B/lrYkN:HsL$anQl"mQ + ?TRd=1(:0\8ltaY!(U:=dZ2B;B*/Pl/-Xh?PaYk0]E]^Ta4gTO06p.:kgHT2>.: + _!5FPO:a.n?u=B7W1[[9UU>4'eCZ--Y+TAt7D()<]8VECjEqT/]3`Rset_nV$`Bc[1N3JcO + $q5M;,V>Rcr(J[/["L`7B>`e_F_YVma9LBCsMfJ1Gd!X?DjYFo[a/.Hk,?,5&g29ZML0S-@ + aCM4X;#.0o9eo3L2%:]KB:nVRu)S--TJZBo.\nhgU"'!(d&2cUS#,ZsN?tr4NNWu5^^sk%q + ":[Qt:KjE*fG\c7Rt7H0Bq)150`t(ada0hQq?E18Uu5r8)Fk;?83kSl1is0G"%HgoBXes1# + )Ro*1N505%1ND+^sJ0"*h!:bJY6Ca#TKpB*QrTP'W0iWaUXS[`VpX:4=q3P + `p:1QOR$E.OM:B':,AY6-[Jc#>I<%+F-cRNQ3e)4Cb@JXJZ?1E6hB"#.O7+%R8u#$ukI?tI + Cr[f<"0dKV7q@_r="_$CmZJVpBCW.Bh>(o7P3@Yt@<+C]X$-UW\,RXp.n:@ + 2*$3ki"B;u/^+D7,IH!Q4Didm[,);]q8f;]$uTP%^u6@k[FIG+6\:TM.qd?/;tfEnc"Q@klLdG+6asJPA?"A<0eiPOoC`?l(S*G+ + 6gW@SSa5CI%VmCqM'Kua81mIWCQfPJ#V2fTW'Du_>^L-lI#U6+6rCqYTN0I*3q-=6J-diBD + UD5Xp=gaJ$+S'mE_sDdeLU"lhUOr+7(0M_]S58>fAFVF!/UIm$[jr+7-iCboc<8I*XVY'1g + Upm5b0r+73M9f,sC8SCof[ZZ+bCmFhKr+791/i?.J8]]2!^;jcbjmWnfr+7>j%lQ>Q8h!I1 + `o>H-k-i:u7]Z9orJ%\AJm!n3.p`CaI5L6;.56Li^&+fQ\cg^s"&4$=>6k"[&q?\8-0RobS + re(OQ&g-=8&>H4=Cd6=_s$ooe*[B`rO + HMH,/&+l)NdU<)Hd6T0,,cHukd/Q``r@SWQ]KajFs%H:@1+$PYRg8e*+6g:bVZHdeL\ + c*7l.S+pK_1lN!<5jTrBUte#X?fms%rms77\^AUQg@p+6lOLWF>e'#Psl2l>V6PkpE>D&u8 + M0rDSdM<'So@s&Hu8=:b(9.2AScT=fquC5MXt07rHf(JnitaYs'MQ"Iuj:*3>`\=@-fFo + YbD'68,Ef=lpQ?.1&f.)Wu#7sD3'bO4?qL)s(#XE*Xj4L1%=c9KKFQ#_mnbrNg,kT' + \L+6Dj=Qe+pq>t_5@Y.`GSZ2S6'q2ZW[7GBn*EphcL^N\gfJ!puKBZKqFGg@,c]XO@$hKcF + Lk1"mMEW0i^ZdjCUD=5+3^2_3haWK[sJ"5-7q-Qe\IBU?YS[&0<8,;%kYL384Vtg:b"9:UF + r:l?Ek5h5,$%[%R#Jh6$JB.Ki+6XaR_.Y\2+S6e0:l,R@oJ/YXgaIjFXqnnL@gl4*s$^Gr( + 6A)p%O&JHLTpb#`"[M^dJ3`4,Q/e(LlW2.CeopC74=5hVE`M-s%;fj/&f$1(ZPp49V_PBO7 + ^m=j"tM1Oh*MB.--siqKj=Kp,kiipE*oCJWOm]7Z[3`5LbXKOV0E>8I6[h,bgtSlAU4m=Z% + O:m%9LtN`+#"C-U7Ys&UtY]/L=QB?j_hFF0V9HSUMcjmj62n=uqVE=[Jn3)^R5:^ + nd[H@sGRsK.5M)^bRko5+c5FY_B.F*,lkk5VM:XedFRYl#dX+c%0L+4:s'q8`IF%`l5j0d) + T^8n4ZJkf,-uBBe'M=6[C+R\dq`pouFeZ6H5XWV^:*9+oPL>n#eQT(-,\3IL&,Kp5BtZNCX + d5OlEd$2mrB[B)'tQ=rOA\\R:N,:mVc=)"iERs7/#@Wu\H2_2lKqr!c<@Ih''J#QIdl2Ub77t*kXOe(+V4J)HgcscM(p@S,da0XL].+b\,QHIR'V>V + qI>IOrrh8m)]Bqj&5F\!43')UKs8Q8O"of-W)X>%j<4K@GaB)bW>1;Tb*u?#<.Ep:AW7tjS + &Xa$=0">T5On/+a>1&%A4;*TAZ5XN3@lB8gmk_eQ;O"e:1<7#Z'M7db0-iZFY;pY5P#q-bk + #^hk,%jfQk\[)q854*8_H0]]+AFF>'l>.qk*KhrL^Y5OtpWL63mLId@*+O;!fIP)Y8j6=sl@Gr?A!9,-q;;A# + ggFX=usr0Och!q)>nm2nL"9UT4fu]be](GjGcMpaBoCU5d4]ifbHC2lC`2lB# + 'Vc2=eF5/.4h)"&5u + Q%$)UY0-KT;OU\83%bC&+liS%,0j\?h,[inY_$hb"!3TOX(o*$VOH$EdX0\P`pJA!]N&l6. + qL8s>^;F.:%Pn0+h&+hOA/X.[$D0<]C?YEC-A;'`__VaEfM,-(\f];h%[X1HM7YYm6\FLo' + c2S0X.6dlFM>X\^"]Y%+SbYk;g/#GB[bu;udHdTMX=0j)e1ITp0@W4JOfL<\;Vb9(qj4gJigqDKNZ + grN$']Qh>JNb==7T2aS>=Zt[k,=d4`fj&oe*8dBKuV$s7a\-Rj6fD.mmdD[7hV2Xr,TP'F2 + 'hZRm_Cuq7"CcpIBl2)nYnq>@9?/Ho<[[^XR]sjF8/.J3X6g5S;5U,-nY`CrfOm$k*>2F9` + hFLqnt+^mp:Qe;LC/i5jCW8E:7On`<=D`u^2%_&SWS/"D + 9(MYL*3dT3]*Z(gZV0VkPRKe#$p`#T6 + HguIbWJ7gCdbOD]Y!7F]D5FEN60#.b,%5D`4X=4b#cO9,Ghcn`;IuJA5OZL@&=oB9ghrT<) + k8]<\jj+4ZqC\;1(g.gI8L)-Tp3ReDlVa]uE5)g$cRW']1K]L(F[&iK:NKdUGbVs3H,#kqS-42R]R7K9`';t + W2Hsfb$YYH7!QZena>qX=9E`FA"eATsX-Eq!,RHChb/gpt8AturHk!QHeB%OuaUJfje.O_( + hT8/"8Q?Kb//G`\kfJc#adjP9l"i=696=cu;, + cF%_WBua`6DEnrURf9FT+RTr_X+JSl_?f\UlmjH6Su/K1T82qWY4*r_gJuCH`f`02T)o76c + ?Y_UqT4W^I.QU6j!u^F1BFM\eM569N4GS@S9C:7neS,,kOY_e&"40bWJa/`73(#j\3=Nb"W + =@73tdT43kf:E^a6a`GF/'[+N?Vfu9Cu7B8>[HeD-Jg6cDWc2:qo$@GSg@EpE5W!$V;46!t + Bk.Xue7XXF>NFkAEC;-cbRRCtk*:>['m^4a0f#!e3;c^pp==N'F.J$`N`-8WFF&/?K:TImL + ]lq`D_b]!e^[2!/HiVd3!a!VK2SB5po`Z@)"tPUN2]RZ51;6j_^UIEk8DABja>STn:,tpFS:HY1C<:&#^qorA^e@bb$od36h-OAU?RPMS1c/ao,NQj]fiCi$$gJUN>iD12)cQcIT,?MoSDpM^.cYHb"CL&DQF/i18='K,u`;Nbjr`>:q=/>oKZ-1b9Z + 9U9r2Uhn,?%C:=J[Z=!2_9hg71.[I$b'p\[tKdoKb%:N&3%N>2r'U!`=pnS'Y#+t\29Q*"W + 1AW))u^V3/ZBl6JB93Q8mYAdJ]H\%@OaIRaO'_TZs"0Hm[P4R)4h2=qrJi,FlW_Sk@h7dab + 2Bj.\mCUWXs^diGF:/#U9XWCq*0dq,Z1F0.fnY-<>YY`q9)/$m.\4kit#gLI_IF[95`7GF" + A6s[DdlpoQ:^!q2=]IgnJS\V(=_6Pl;4H.Wdji'qG`\OVo]^ma\M_E%lb.U2G#okNQuX'Qb8Y#"lsK)nf]E1riUgM')-J9hB-mfF23k\q*k:4ei1-Mo-4$GjXr:u2/Rc:O>4*Epc6$`UZ9'U#]0d';1r1mV,hmY[ktK($G*Z* + +)e,nk1#=Hd"!QpIcJ1=L>Y[%Z>:I5I1hn?pl8?^I1fun61uk+\ZZ\E8q`APgi%s5jea#N^ + ']cVj6J[8bCE2SE[^4P3lYBbmjJ;5)[.%VCQg9r4:Gu!E:/ED-]O`A_jYV5D7!^=pEnbe,XI0X?n#\G(`jsn,^[j>-n%G6-SAp+![^& + 67urP:D=f\\5^m2"@Rdq8:T\8DDrXotB31"fV`m6(8"S3>1F[fc!O`2T3^pQbA[=4E",q=+(80I + @SQ:nh'W@!:"/f@j)AjDE.;P%05412$/MaRn@m+ + mNhNJc+0MsU^a?h8hV"VtJ[([XAJo)4a![KS7\1/Zo\B3#=[:>/jK2W2T#o7l(!B0'\N&G* + WoH2XL9]qfNgJ3V+_#Pe]46%cuZ&4-4*I0:V-#E^DWmKbk*%UapFg^nB,)aO\46)5')-@uZ + !K,>`J#VVO\"jfPO&6T,Nc6._;+JoDmJdL@eC)ddX";4!'E'Y3o#U+d*+G';1H3bOl_#oca + g^l=G%+l\RL`g"0(4l]tJP05Z-Nj`A!\clndKgA8"/(1s+>O31^VKqI5YM_$"9[X+J7p&@# + R#_Znh<7s>ROm=IDBNjp=6#jLNg*=t_'&^:H!p)oAd,E<&hJiI.u%ic1r"E-RFYX[f]#hsqhTT, + <`@L1T__&CZiX:_C_)K5``Bcr.f#\Y?8HkM#Ka<@"()2KH%Jf.CZ#\9T_63q(oL,:rC(oSU + 6Jf&/EKRt$q63psAL&)'^+GK/kJes4dG(LN$+``/N&`O(#TS9ZW>+gSFMLlUW+_60?-m<"H + +GG+I=!333#Z`(%>dAqr80Ll_'aPsY0d)0*#Z@=L+p_&=KbW6m'IY+iJeR>k4Zad>l3L--K + \?]++G@u@JeJD5/qO_W+Y,u(KVA`DTS/I68CD/p+G(3h+WQ(GW$*$S+G&0JdMc+Sq7:%63np2Jmo!3TRp3R + JdEhJOFdc%+H4NWL\Q[W+D<5;J"[,D(4W$UqAt-\-i,3g,,i(.Nh>Oorf<]9$3$'ZkpcUql + fh#BQDQ&+@"5%dqJ(MWkPB8/-Y_jpTW"dbLjuAdqN??-BDS+4.I)efWiINDY^`]dqRV0WkP + E*:/8H`\[&p8&fRL$dqVm"-BDUr?0'g[R^9B!^!.V4cq[.hWkPGqE0l1VHae.p^rlUl"\c0 + gZh:hr+76?4g`Pq2rn_tgo75dZa8:aD\Hi"rJ%:@3S:A* + l6_s!.cI)XXTC1\;miV!!rp_*!-h?jl%o_7JcY3>B"8qs*oB8EbYl?p!./b7*r.'S!!?^M: + J"U6LJd;,ZK.&GZ+Ts'EL\_`-k[8n6U&KfPqAt>=JIs(o9T"W%.3IcXJ"kp$N!K27LFCOh, + D:0"L\a.Ukl?4/+o[T/qF6;j+XJDPTKgkLs%5ZW.M)0Q5LR@I92TCU-02k0#PqpPl'QstJc + ;*nqJ78<\i$AgjIHt"s%_VQ4V.2[5L]-%:]pY^8&Yb-PX:(\7\M + li_gB62n]UqZn;1."AJCg;B;1&n8cWMN@Hb5M4?_@tX`@&,&:RR1giVbc$#sOoEN/q^ruW^ + ji[?)U0/+3+kqW,*59C5_pQfBT3Ir[$I$,>'j$h_u,H(nhem#UKD;+EN_8)^e0CX/0Er`ZS + kk4g=pC]D<129PMuL;#Pg](c.0J9[een9q+r0p8:Qmjr`,IEKlJ^s`jkaa=U4b7+6B/FQDZ + :q>g&UXY'nW_Jc8u2q0X:Gq9qB+;X3(RYBAt]J!f3oA0%_XG7Nlo46LqZ%2l!"0-Gb5Z2SN + /q6,(I1$CbJW:cb"fJhjgi/dJapR^^0HuG;A^6d8ia8+7-k?[hrkN)R`ZhT#Gkjsl,dQc&_ + !.XJJ!l0$o!oR5Y+6W9GTD0W6+J]d*&8DXsdJed=q@Oh5:(Y8J8I;(Y.YUEHJ"e[sM+@CiL + +pI_6YLHLU!be?khLZ_OoCC#$p]W2)C._LP`eQ59-Lt*Fh@W"h + Me3@>PDs&Gim=-*#a.-2/?PnL)t9#Z6Ded2:XA,Tl[3Fse=`4D:unS+'WW^#(Es'%8h@8(B + t18\U+>iYd)O7h*]VMs&oo4Djaft+o<5Q` + MLS]/fum=fXu+XcAt$f(0;ZSUgaCQ.il4B5Lu!_4%RIHGDWn"^rH8L9VLCkT`_6s54a5(pG2u9kh="O8K`F0Q_ArGXXfoo`lRorYNI3DF-lf4JYrXN?@qV'&n:;, + p]?=<2ufjO8NLHnZFg8P*6n:Q$Y#Or]eIa%jK1gBp'6Hd?gX.-KP*"Pia70b0,KZbgHHVo' + TkRF&+MD2dUC?"-McT!bJ5b+H.8L\pc1I+S$ + ;_,T"m<.Dp/ELSGY\LP%.MSB-CVK7`sXJ\Ig-V1AD'\gZ1g^<1e9($k^c8*Ymh/1e$.fssR + #2U_lYpYpq_eS'6Wo$GpHYY0ZXo^U9#(4]'ZffuJBADHu]&C]tiX8k!0sPqNWfa^3%[@`ZqVnd0@-9UM15TU7:0'X'E_0079JT$,6MoS);sagC^ + LcC>KL3TVW",dY7CDS6JmW + ra@hf)2nLUaZ'<8^9fi/S6MbN@KFV0Q/mdZoU:R:.]-q(M"Bst`Gmd9bLWdu!%YdM,MWjQL^*oMcZI%jWXQg:l%?(. + Blif!oZAe4[l`fZjB]-_to^%D'm*@-OY5Z*bXdm*:m+TE<,VEuYo^nV5fUo6 + n:bcLcOgg!JRbpgNSZZ,UEd + e6IN(Q@^P9_C@uG+VQVp[O-fC'N&VPk`B:q%9(Cl6Oem.&,%`BMkf+VNCZ>5h#*h)m)^Wcb + CGQ_]3:-*3[O*n'5cGd\fRp*O"3+n,FLa"!D56+KE + M)1(aO"'`h2LF]'3^7q3_s,9h(RG;(Nl&i0V!h#gC0GqqSddR+-BUBF44hbEto/c[m#<@+k + P-0Nl,0OZ;IA:AK2[8%k(Ed$H[hZo`86IF( + V^g&o.gTo@9>H=G]*je:$dunJZGsT!R4Q_./J-3bOOCm=U?[p"lK$+u1?f9hm#BH`%eB9'. + .%O]..$BP0M^>!Ym2u1$B`dN&JGrm;]:hj*l[A,1/T8OOXDp+:GWE*Q.%Z*;`bYV5*ke,a_ + ?/[O[h7U9/R$:2,([VPk2fWt!WSof+I5is3T+Qb&msOmdP5' + c5),m"BMBP"mT/oiE3f5XQbqpS>XW%eU)b-SAO1?4U]PB19V6*_IZ-:c#AGm+irDVG%&)>c + GN?1mW*g>sCDsl8p[3'>]+1(t"ciDcDh&qdc+OgtW0G<,m"X1)h+nQu6OWOOE_LgX;$?Gi"Xh7mCHCCV&LL6g4)egZctb!6q'T![-]iM%OF"A'2WN5Aj + (FOU?7Q9k2]So0X3-M[--qikX9nO:r=iK`p$=D8heXEE7HP=]. + NO./hl4BYiNUf\Ei_VlpUFU;Ypufrf?XQ&/,9[8IlAk6Vrg/&MEQP1E@(6+]5ED&l/^4ZS) + s?1K)O8'5njn1I2]G78rY,'TY"RZVqSP=&fbu'rBe&o48[nBUeU1(:.url-C.sjS!M/3"L% + =nUif`LH*EY3>%<4;3IV-QI1t\q2q?GnYJ:K18C,3U[PNmm7-uZ_-<,B>Kp5S'@0oQd9MQL + er=Sgc#fnLfVn1dfMV(hWhj$ZWnC+9+:Y!F(&4kIWnS.!gM5PPMW3D#a!+7&@8ngeQ5_&Ch + ]oQU,Rs'KZq"Tt9&F_dA!Bl='NVQ%>=Irn"u]1FQ;L,s,=`MR7n# + 2;pQm,:G4R!+]Pos<:<$dDa_f.0[,YjF,sH>eS0+_D00]1FL?=7l0WUEr>:l)ped/U[q/\9 + B_bMqq6Qsp24o)4X`q%UUc3idb#:DP=Su+?e^)KFF4FO%f[eC$f.l5iF6?i8lI[cTfTGc3F + 89.Kr7R//g%#;RF:2H_%bgC^gJShqF<+br+P]d9goC$UCAU@7j[5BuYcp1FM]$!I*s@ldhp + >4h<^0$=6K$Eb7Q*kf2GUY6D.RT6#?5nZ'ROFe3G+M0B#l?4$\m`F;(7N+\^l;B/n!g^Woh + L8Ba_ZXm1fG7eGbQ + ;!lPT.mZS@QlrS*V>jm^Q12d<92Zh$\%e3IWnZ7dk/kE4n#Vb2PIAsS]3@)*C + AB_,8mRXg_PYqQ*Hf\S),>)dVZ(_qHMLle(UX4-msE`*Eu2^o[JtWbIWZIS!S.d0R?)dlP9 + Us3,Duc_=7)G8o9shXhiLOhlSF&H>dN&hA;a0P12aLFa[[joJeEUC(aiprJY!LYJBbQjl*lG4AlCoee9ZM_SLO\a1_UiR& + aRSSf4/#?)`Djjsi8P>p806!@qN(If_&OCE*H&YCRVt!]+RYQo,NK0>`ohfDqXPKl)Gg:W1 + FNjK@;i8p>$Wq6XX0\@?]#0[#Vcjm^XS7-HbsM;E8Wu)iPB4gqO3#lJl'+,qf!h87pS.X*p + KcHT55AJK=^\8!"dYARQIH/+$H97fGsOH=6(*8+)]b<1-(*+0R1_0YPVlna4.4S_RM&&X;G + +5TX$mspRlCR?c4:*H't!`JS5rVK:iPF+>FcTU8+&GE/jZpj0:jdjSog6hYa`+&KnNF%KTr + NjbItDF9A!L_Ru`;'abGu-L=4_hG]QTkUPHE2:2[`TcGR6i2i-&9m4eP,S%FNG8FBY + U:tebYHc^2NY>NrY;E2,*He42K08qbGN,4]l36tP@do:SCb5:Xeoi$l,@?,D@bh2;`>Q^$j + T2uP=(U%hoqQ#^%=SNJepj=$C3kY8L_-)*&qA^lG;m<%M.m'7YGZrq@]&?lk6^.82`1$(XP + MMJPj-9t2YFJg''D#O7!L>[Qflt%n2no>s%q"+9?kaDb>8`eedbHe56?h.,r/G-bQcV2LZM + C,%LS)^)1!3]`@s^KfHUUB88]rr!bAq=)(/qVG_.B.U-bEYSgL8=uK?Z02[[)&g<0Ms!R=i + \V[s[FRPc8?NIt[XskF)KGRKj5tNG;`&C6Zu:C[odDf=C[#U4!ojG/r#L1PhSL]/9?3pm%X + \27rXU:q38t(oG9abLC_4;AT;G34Yn^&Z1gJVU7K>(DkX])cTl\^:;jp%m?jkb%THW>.H6&26k&=^TI+JX + uo!4gdN5%lWi8n>nc1&>4n.>W7Vog58UR(3s/gEBjVPX?X!u+2oOG%a['"Q!IGbngkB/N.= + KA7aRCde[Nin[-U]o_Z.u*BgHV/%!Qs.eA6cNmftkkD;b$]&SCeq-RpTcTd=Spdc$O6g(3r + JpICD1#T+5A'<2Z@MXgf.3cZYZu&,"!Erj[PGTBg_Gg'G*d,%-4bN"HR'6U;9f6mN?LI"EK6L_q<;# + _0_-&(!]1&GZGl_%0ZS+m'&TJfWd)Cs3IV"?JgYE'oj$j.q8H+GTY7\d30(_$uJn>S,Eg2! + prjL`r>qAqCn.L/&-g`rn+_$nt$R.L9C5(SHK7+`[imlG3j)6Hg"G"=s5>KkMW1#ZQBii.W + gX&>K@V5md$IGKL1cJeK8[\03Bs"=0Xl63ng/JhdTU^rRXSJd?$4KRsI`H3bW9JbfWo+G&e + `*^`I@G(Kri+EE&=&K1ooTRjBS)PDYU#T:V&+Cp'.-m2oL"nB/0(Ee9D#SnP6>_%D080Cf^ + "UH5GJcsM*#SNe]+p\dRJJ>&7"=PBXJck3Z4G![@q?RQRJD(8<+Fq\_Jcc9$/qO/G+=_sEP + F;a`TR`12#bZ^$pjrP1+<5t6W#uq/!H^@."X&=f#Qu9#4Wo=Va<1hA!/Ma&9V?nK#QVZW.K + <7EFX9Qi+6W2#I\;JU#^t7E+nU@.-mA)3*qd)MHQ\*D#^RVKRL"1@80QuE*XEqGJfWEG#^2 + kr@L.+LLJeD"g;kGaJfNttb^phCq?V*aLDgG<+GU:0E*Z!X^4I=+fp86W$/*k)L+:KBcr.f#\Y?8INX4[a<@"()2KH%Jf.\E#\9T_63q(oL,MSS(oSU6 + Jf&/EKRt%$H3dn$L&)'^+GK6&Jes4dG(LN$+pNXlkTU^jTniIaJ"dib,`3eedJf)ski)N%+ + 6`h*OWWg7\@Xn + +6Vj;T?&('O9,JKL`h2?EW1QFq@"J03.IV-5(o#l(./@HJ"a.HLPP^K+6\N1U.AUIO[:NdL + qnM7nc"K&qD9;XfRKZPIZBQM5"J'>+HQ/N5LM7c8H\o;&+l8(&opf@8Q^J=;>ujdqH>!*E. + =_Crg4o;@SDWH2%T?-5LX$?9uHoh&+npIe"h"Zal,B1iVoH>qLB[Q#_/d=rh5,DLlc@#7O[ + @D5LbMh;M4p@&+qV@eC9X[MKr#J=YQf)3iO4dXFI7tri>,fYuYVi>`\Ps5LnEd=/5_$O7e) + hef:ttWuY`PdJgHWqTg8L7";8mf9g>bD6;@*5M#o8>\!_QO7gd_f1>pIF&@+`p#i!]WLU\skIoA]l01^\eq3XA"Rr]ea8s$1)m!l9+5#,(#uKFi\@O7Xndd + ('*C0V/qEW*'M%KJ5gSr=k=b<^4uJ2ho]E*^f.B&/!Rp8#-#)`+Y&A@U)WanO=q&nc"bCqG + W:A=tN1a]bht.@SC;116;(R)C%@[NNp2Wa#N4Pa81''l0^[SM"r-g"G2_u&OhcR)F)`!MNK + YP8')Vr,h2&X;t+'hadFk1eP`JL&r_=@+o\SKqR2Y%SlNqd?S#cYZWP-DA.S?u/nMu6QdZ" + E9M(&KL\fdJl[!,9R@?"pN746%bC=-4U1T%:O%)][Hs'2V$G-;D/gK1^6B&37^YSIS_=D*T_X@] + HMmlZ*PlQKf1X4=MQo\Z/t^d%2AS[7dQo3*B5FO%Mg+82GZ>0$(0mNW]"/deJc9oN+r7`qa67]`,KY.tEZ*8>3Y):5EEdApLU2X/c#qRE)R\t,;& + ^mfEdgN>%Bi2dgTX?6tORH:oX`h&(/Sst0)X/CplU$Mu+`!Q"iYep$)QtD<3Vjf.$M + >S]pED,`&hBe&F/CV%.PKL+3ghP1W\:rq![B2u,W)H1*CZ[%/ + k;8Wd1qJYpj6IUEO/WNS.3YW=6uOq;X>N@F58ooY$C]!s=IVrd[B'!Mn[U+`GW"Hn2=k3?W + hl4(VoQtjS6KO=lDpGYe(l<0Ub)-]Y?O+5J(F76lQ[_6P<'S\tjtj`*4!& + ]LO6r^dO*s7%.".b#:%]bWJd)4CcE"lQ[^f,u"ciV>JHKbYp9cA2:gJ4T]:['@KVg,;1Q:0RZ?\UO?o,rQZg)tQ!K]JYi^QR.W(TRX>NM9Jk.Z4W1ocMAb\J+Ila + 65E2%4B1+H=,5E5)g$Rf%3@/9AQdZbK,'25=BrHX-stprAm"a/Qkp]DKXlr1$FYWhIL(qu= + _G!6?&%a==G8"GB$.Kg8Ul*DD5=J_3MP#frMBaJmt9]"]9hNBl!R5idtI9TUf6kXca+aXe' + r`"T5iJ2eO7\1m']I$ha/R@f_4_5s-B\c"h:ff6Y*+eY'p68YK)7&;$]VClTq + 8pi#/N$ui;(!l@nf?j9iP+>FB10WdChBi4\9uhUt\>Fga>MG7F^0_icmo`4"%]"7MO + iO,i^9Uj>8'5cDGeVLlVra8^nbPi + W70n+%cYY+C$'Ar?40H,Iof;b$678D'c`?-omD"b+pLZ[442d/@k!^b3Ub'r@YZ40bPH]9/ + JFt67L)W2+qI<,2>+2'SK]uNIkshdg..(_DdZDc9CX2IM:BHhH + '_4+so2c@mf,XIkXS176pRAPbW\aKIX'rc^Gu)t::ZH$$o4LT/I8Cr5:b-qK5s/ijt+3\pKaRef + B7X1EsAdnQuD?*?<.YZ$7\,?;$'F?J;FZU=+C>AZQ.Had4=5hh_s>H8H"]=gm\79eipg[cB + &X2D48_6OJE>W^$G"\r<6:L%tbgkAV\P%Iso/]DkV4ZlkID9g[[cb4bReYUZkFjdUsgcaHO + ^%4KpCI,ZRj%a+FZut*0Ms!9_lFs!P[,c&1rG&_Rm`tcJ:CV2!Q2fZb(3h%VgX'j1\U+$CDl,T0ICLV^B`;8&8mbDXd(R*k1%dJD`\"-L_fP:fS$/n: + 0WFJq3Wle3QM3O>6ubFU@=VCt<0\g4OA4fM?pW`(.>dl"5U67gSa!5jLL3nuc#*BDk+3kHC + ogJpA2h*&!t5u[Zj`&5,*-+d,4]YnSTld,tkmn9\`?:V;4nPFAgWs + c:(ILeMYM"6det7buJ4jeT3Q9]idL_?/6f2h)/HNe6ouVin\0R?$0R>/7h"5k?Xoph6gh.K + PA/i9id0I^;GJSgTsqYh%MeYr8:jM60cUBA2Ih&L.,kbLLb47h8W"O:#9YkC+0h`[2ug(%!UVB(["NC`59fe`S7bflN*s38D-o + `IlZYr]BC^=M9Am]q5AkSj5Ha5um&67P=sOI]ld!YZKd#MSNVT + N"Z-!5R)fT+25en;J#B[Il8T!p3%k:>p7O6n3Zm55_t7k[`30M0:8=`2i4/XUTAT^A!ei70 + "/jWe1(jmCAS=p69W(>e"6IC[(@d^FYBhVq^_5[&jqBKGt]/Mca3*U%uADN1jlG)G9,8FF/ + QFKQp'pIoJl\9-kr@\6Hb*gG/DQ0Dm4<-M9jN\AW&Sk6?N"-; + rcR&@p$CCO"^#^[@9&q/:%&0i5-%]?XB"tr1`$AiIG!+.g$k$,jE'MFKkFqZNb"u#jL5S![ + gkn,\5*mNE=DF.*->u(Ebi]0=$IEiNkS;\^N"YuEK#<^'mfVNHdDqDlojlMdq\"+"D.3/lu + dpN*J;_(K^>9*Aj^_'<=NPN%d_`'nr8RT->G*@.oB'hi&5InhUC.:0BX)Uo+op)f/$g;Fn8 + A]q.6'o/Y-am&*K^RB=0)L`$oQrUN=\U;SAoE*Z'V4^]=BN^u2EdF:;@U(-oiXkSU<(.=m@ + !@X&`Y1BS9*`/l\n$q2@kLgXZt(k)Z1JVRg@0]WX$!t@N51M@V7"rDiETOCq>;M,&X^qro% + N!pk%JV2lgkSQ2^%"X#qYV9#b'*AA&_"Hn.)?p@rJKS>^4:DAq!5Q[O=8g/#F1I)Kn)t_a# + 5j%dn$"b#%J'[L5i29L)qkMhJ\<1E3#g$c"*hW<&3"tGh3ro,?uF%)Xp40B\ob/86k.HEF$ + SNlkSoZhj+4_5DAX=FEs$?rgX%kF%Cs?,[bu#$)eK<,J[PRct`J[/["L`7B>`qdF&YVma9LBCsMfIue"?m'%W#%&KaljeT5L`(AeQUi! + 7JX6q2#$[a3Z3FL*AY_58Q=q.HJX.]%@1j\oH3RFnASMEM+D"eaJX&bD;Lt)=),D4pAMOHf + TOf;O;4qJ7L4PR;)*ib:kSZN1+CsaA:*=*$##-4NbcZM\#T57GP/nLk0Hao##"bIuOp4J%- + lF.YOm!Z&JWZlK)*/mN=p@DT.Ni2 + h"t"W"s.ELV(#SSJ#13^@Xk7fqJ(MWqbhI*VlBNIJ#Gm6MLVSfqN??-Hd + \@\W[aI?J#^QcZ@AofqRV0Wr)1D:XK+qlZleU:rjJHbHKiYV9="&:Y=JHaJ$7ZA!5U@J62n + colr!c%+7+"Ha<0c8B$)mQ_`3.PBDXf@Zj6IrJ$b".:#\#>;cqZomVQ##_/L5rdfk$#_K!("sDtLKa7Gf+6ZgVTn32tL\`!DW-Jpu,YUGM. + /u^+6k + ,$W58J.L\d)Sl:OFC74b;Md[hQGFFU:krhtVKU`\lj9<*\FWtfZ\+6pdoX$Q`eL\eM&lKV< + K`I+m*+TGPRrF6B2O?_;ms&k!QB1mrO[$IH=+7!0]XeGa'#Q!:!l[XJf.?_4X1?`"rrH41o + gcsD@s'A(kH4T]92OD;W?]4u7YOVV-L\h2rlkk5VM:08.@h)rV9HoQV.R2Rqs'm;PNR*>?a + 4UhFAE3WP&,&mcfi\8cU]$P+L18B[X#F[$rLFJmH9^*Xs(EYtK="[>dTTjJ,")9c&+V89aM + Unj/QJjZQF>Ad0`NF$r07f`=P0)NASgb1[C3A_5KVj`-c>kT2^[P?>J#0!D="/2nt84iOT- + ej$,F&P%cU_tM_%6nZ4HNMjBR>%EmA+<3L;^39A+f:KD`NGo*.G'/#dY$Z.D&BAF1D2Poo- + nJ!lXFmTo.nGV9Nc]Mh]mbuk!'Y:\iukFhI=UXjb2oREA#rbu3(gO&CCn\/)Mq9Ffr3b/*Q + ^=V$8+-Z>Gce.cKo?';D3<),pp&72^"T]`&#!V)Fr/^IC5L8[*JWN2)5tER2OBDNKU\tFYo + HOTKj<0+POqU/m5!RF#s$Uh3'uK`U%,n9HL?Qj2UJ_#MiTr[_&IK^MBTWo?3<)Ppr?]DGRh + T>%]WNQUgmZMt)*J7S(cB,=65hl+DMD&cS<\Hu=sAdQ9sTn=W;JK-0@mJ#IVl+/B + U(OO>=@.8g)@a822Gl>B,5kp7W$[$[$aUjoDLri8QcYESO';+aDS.O@Ep+6rcRX;P:oAW3s + ]ZJJj0Q(gLoR/^?qra/>JM(tkqb*bneJ$*ej\n6WARWDMFc'c%!f?!u!A,Ul%)8S<\2!CK/ + WJ-E,)F)l!s'dTdM9e3J4m1&bA&J8,&,&CUfd-WjoF?K"H4rVJOT/pFrL4>kEC;4'';Dj>T + oaj?6O8<<+7t#DdQ1"Mg79W;"HfhtqHHVWNAGBfNgg\KE8(Wr4/]IbTXQiB;$oT_DhJ%be@ + M,Jg`(mG7<#Ij_>dO#c!,3`9!9#iiE/aL@rp1dZar3H>3ft(F]H5sR.DZl8,[V7($XPd])$ + )7)5h_srk[5k%eC`QMDh?eh7[h.A1X"1H`qV9SAN- + frjG>8qS7+ND=,T[\$.mu>QbCc@(?6H[iXVc\31)"EZ%6KS:B?VX**B;/\=QU(8D^c5OSd8 + ]TDt3??CXX;CkB]pU9hR(nVK1U/O[`Ah=Pjom\Xe@#Pp+1T%\15O^Q:_,rHP7gpH1e,4J5p + eqQ/N&;_'"s$S+:.10@7>W`W.$>31'rj,e-:e%h``U\/8dReDgJjO']RAr'njl;s."/%FjBU!4>B + ;V`Yde5+8Pq%kWK;=BF]i0N5C8Zr>VO(Ng$1o7nYZJ1IcK@8COqkVc"k88\X>aCDrq52s^KA%7M^:#)sCOKccihP4]>kGI,e*d]CaHYrgCmK@ot#d]<9Zj57BA + %(@!*jl7D[>4Ia&9?do#3.4MJLbhnCOHI`7"T;I\;2oN#WB5TQK>&7jm/^r/$+GBCdfsqL5 + pAfk]FINGj$E!k\,k;X4W8Ei/+V(8hQWkcChp#\A+uTlK$+R0"/Qof"icg_6E@oB^4rc-BD + sqO/t8O%c!/Ki5@&[`2OZo0bWq(.dq1[PAFipXD\_I#aDM$a/N+\.9c8o"eEp03.U6cSA8Ei*-;A%%O4:(^hP^Yl3E"]9C:9Dq_g]q0@:!!H/mW + T$4,0e\`%GKY-2R%t"YnB7Wa.hH#o`U0sHV5k8%K_#Q#2l^hZcCY(;I\U?k2F[%[7Bksa8" + Z3hW#)'#Kq(;96c#J69oEXjKOZSa>CFu39>fGjj%9A$`JbPYptcOL/)dPE3d,)G.b,lkX^H + lNf=H?gkgsN/C']ANh5Mif"Yn0^[g7I#g2I0k.p35_._Wj7.T])bn#a_O,PiTcIu-uhX3** + `+^(,koB-q6LR\F&^4^JN,"R&#$.o7nF8-Okuc#J2Y"n&ndd#\%%*B_TeqfcR'/F>Yu8aoS + BJ6BoSK[AcVYHOba[tL2U>^"mpd/-jION]SdFKPC$bT!LUhE],UJ3_sc`j2W[+&hd)pc8=#.kjN`uL/`*6-5D$?C!gf):`brR'J>dGh+eq]:ga*ma + 9s#7VbIWc>A,33Z=-E*"?`dj=Kj-88nX0P7]TnL!aD-STThBEKW_C4R"K#SMgs;M]o[ + N4qKbC3h;Q1"Z@1(2hk>$@SNdsW=]ds`2/4c$WONB'#b_f6D^@%ZZE + noru`sraD%S5,&Yj3q2mk6*P05Mnp;Z.-T!>#nIOE1]G#o,/a%l#dP0ki!iXB^rf+VGG7&l + U3CF"G@g==;*)f0,4^peo&[1lZVM;,;ON_M'm!RoWMpX!`:WLu2Qp:l0lK!*Eh/ + D>(R'7`dt"$PK$UFj33eB$nW,[[#ruie$,UERlbPr)fN8+VQCg + &=$02Zr/G#0&^L=edlhPcrN1/iOl63njZ\ZWrlps!pHPdKs5MQ/1'Kq-$?NK`!>H7& + O;K]+*?l%<,"HJoO=;C#/9fGpYRTCE1-FEG@g!E5"B%d;&krMW;$@3@"\U]1NXmHH'K\![- + BMq2N[4o?.Qt(_#EjL*XucnA5X7)_#dTL^c;=mC<^O*_$.>M=mUll;:C=D2$M(KX1:IX3]* + kQi$gZ=+Y)t9CU0Q@=O[05 + o[&FSXE0p*>.eJ@WD,4J$619i>bodK?-1g%"C1XS?B$$S4'75U,tQuPgBY;l.F=S,&:("6S + ,'4dT=BblnL(ro'pPGm;3R,m-sl=;Zgmds)&PQmSOD\ZP@B.&T8B + un$*r''.7_K,B+AEnI[JFCl*,TNt_7%o!(\a(EZUX,4i8k<;.l2="_]O@rlQi(:@Xo'k_Va + B,>K'2t(ljF+kH2I/#FTpkDXgm1f@,QT=N)d^O,C'Wu2%`1>f<37.rc42F-3/77GcuIJ-@Ac5bh!E8ldcA]ncI/ib=45.C + 6%\5#TUns&6/RqKp;jmH\0DJNpMWG'e_kB*tCFL6hq\@jT69Fd6p[]<*1Y>"AZ9fBOVBZ!Q + G#4#$r0$_8Kf$;,bb3]i@m;+e"loshIk:mJ5`E)EGY4h>5,4I.'DsVZ:cP&*1)/uu>E:l-[ + a&Ymhp+L,=YAVZF;)ih-Fd%rM'o@)KN;6>>q + 6Qi>eXLatT$pbb)XT[,26AsD]6J81>ITZWk/hmEdp0AQPih4V0BHKKX-MiSHOojW\%.h!^R + $\^QZ-F.QJpKDFg>p4Jts9:qSIBb#Cr:4Bk0#qRUYT_>7sG\\m(nV7h.^`"R + QG/^3NKT8)N%sHKufT_IY6P,RFUq!4`3`-Ktoqe%_PWE.af*/$r`MV;0EA&[R0.b7,I5MG8 + W&H-Jf]>"Ba,[Q6c$Gj<=%B8MrtlUW=0I)jO+jVA"d(O+G$,cc$71iIWN<*YP)V[&/ObibW + NEMEPEX%Ne'4*aDm#L"4df#-q,"$$op(a,Q.,8h$E(*GBg:9f+;o/AHOW6jrBWGl6$\#)fn + CV"l'Sh,Z?_X_<&EGC38T! + .U=A;"&Fm?_dtmYhM)>*dCd#D?T29d-:tVYaS&^EZ)2g?oMemOL358A$__[\#1[LX*5G`>XHe&ckGc)u32`7<*qe^:Q<;gM/fHB[`Bcge43l, + K"U5Sq)6r,Vjb%q#6_?o/gN$KK3L-*fOlIN4KU[NWbZm@##eZZJ^[H*)SoXgBuRb?,.Ar4B + ?88S(k>AXcbPq9\/)eOTGg#^^,q*8>+*P@pA'KDFlr)=^IE&>KB0*=j4d^)I_;?i>9UnDE> + ljY*?8&3O!1i*U=8:dMnBW)gr#)K4nd-rkK%1<8)5gXYp)@?\"JY4,uq^`0S"$XNHO>GZ#o + l,JR?tY95h?J/9^s9/?U^ZDg3C(kL8/]8KDh4CNB&%22f`O+Ibtp2k=pAYkRG_[j)3tJ;d( + mkE1DC88"#+J_Ab[-h#$leH+D&6f%(-0g^rm9E^A8%7JX'VS8/Y/,;WPX90J\:iT*$H_TOc + [m1'UC&P.PZg9FR$c,J_CI1,#(#7"LHXA1/<#hZFuda7(hU%!r'tI\:2\(j(=a-l?q)TOGD + 9)^&Rh"r\UmS/d*78/PfeL'SW'$_UpK"r(bC5iW#-s4TO=2m#phHJVY-Z1SA,R"pCT,63[P;#T*u$J>a:jJUhga"p#iS$d9feoG%Tt:k8ZHIirCh#(@\-h& + M!<8/^uLT*Q>'9;#q'#'uqTU'>n9B9bl9SgYK7JXu!Bh,U+\C'Ji(B3GL^+D4DbJXm&ac@M + M,)9ZB[B-IP"TP"lAE1ghIP_#=f)8(NiW#<-[TOum^D'3H5#&GDo]tAoba;M"/RYViZF.d- + .#&'ZAJd,lE#T9.`RA_!jJXL0hQ$o<;8d8fKAi^-+@#,\XJXD62L4P^_)2!^;Ac`0DTOmZu + ?K2;/G_)2p)0CFr-lI#eTOk\=>9ILW#$PSo).nGc8/YlfQ5Q\,=.j,F#$.C.@KqFWW&aj1T + BKGWL?Sti,)N%mao75@kh5rqTBQ+MOR!2k8s?)#C&XD?l$<8qTBVdCRdCEmEh0,kqL!dk-i + /RIW2KKPJ#PNPQDUYHRJs0-lDac`TBaPtXe4nI'X+Sg)U^GTBl=P^n9s2rke>e3tfN;kPH6KZ7#MrJ$Q!L0AWrcgH*ZfBDY)H[&?)uM-5Loi7=C_;=O7eMtek#gDEoCW3oqJiaqU?VQ>^rlm(%JO7h3kf6I=%1LYR4c,]]>+7/'c5+J!Apk<#c!iE,HE.\f/ER#@dr`tpSo8GoZ6fWPiRIi\@f_Z/_s#U>\m6;uXrEm24IrG#ZStZ.1#Pme3$7AJJ" + sjZO.n0nM%M3a7-L/)#Pq1;ks0`p.'"(,`*/(]H7pTGrg8K;A0:')1<)l4)P^h\NQKOsVd] + Rr,G]C@n`1n7\GDn/qMJ.-6:6h,,X9eOs&.9#7EG9L,il%nP3SEaWe(/LV!OV9&rKRZBi-> + pd/R1"rEi1]>:a?U[?gCG>cco`Zj6HUQh'u]9M(4!-RlOkWd\' + s'Gm,I81*`39MjUSMR*q&,$]%fO4_fe&$=T3U8ikbkYb$rJqK_5X3p:!2&lnK$\fW6uWqB + Tm6UL=*!b9#]1$S)DXe/r.!R]eU&rk7Yo[8,:'4Z5OZ#T^K9tV7;MmgP4W*'p_*a;N"m5*2 + @WC&au>CZ-&"%[FcNtC5[i=@LZApg`,Ze0id=ZLA8Wqsb(3G^JcC\6VDaXdHQ9(4BW`XPs2 + Xp?+j0UfOd7cD=d%uujNO0Z-3V-,CM@Z=$'mgHc*e;TX'Zuhd$6T+/8Ei/Y6o;54B@L[TODQu)[.PPUP`_k + AO1D7D[]-+JEg\fMD>A=S?980K&pT7U&?sVfq8E$TG*?HUQ;'POKJWjXs+1_5m02G%QS"\Tb?R_,F + OYRR^p#rTo/93Rde23T:t#4Y)`Q)DJBV0sFnC])j + k%'*t@k$DX)tPOl2o@7(_r&0r2,4-E":6:JMs:-6Tj451UJdi,%B_%hU>,qHkfnl;t.4[-`-FY(;nBD43fUuB!UE/]btuV/(:gKDDOUt1Ls\&H?26-3@A;u-Z_F>oA(e# + hD>:_1a7E+6tgsF6=FikU=(-)ADD7o1q4MVPu;J[4mUu6WH>XQl>7Y)Z*WnK + 3>U_odTF>WYBWaiOZGg?pC*VS+_A.`'0*jF=V#bI40]5Us(1!jp$9DOj.5[g + WKlOk?_ALpo\*[o<_cn_br1iAZkI2kt^bqSjS.a<$OoNRNBe$kt8?n1=,"3'cXd;hdSqe/s + :n8Y_-7_htt:*A+8kXj!BHncZK*O[9uF+HkbW1R+Q]Q+:]X/p\,4S>Wol'tL\$0%q0qqAMB + a(chbm6L+3;')&%EUllt!\nj/U\g];d.QX7S3jYnTHNKrU"J[3FP@Li/*'>pD3AA+Z]2CdZ + r'2M/5pRXA6n8or;\_tn]Hi]S46/mdRSC'"aLiEg]IUn<]-&D0,#$J+-)8ODPYa,M?F#O;] + ZO]rgWmc2/LGNl9k2TuZdV@$1FDEF-MuC3"5#c$>r/3uZbgq"abi79hSpL#-gO,Iqr/J\j$ + #kC1kLIhV!#I#5`i5[hlVG73O[det.84YDr,LI@P,&^\!#k!Qr1jYdUTK83:[Hj98(Z#"WI[aF"7aN!UN;$7jfe8A#@ + %bRI+rj2gN(Sog&[%$FbRR*qM6+Y^YZ]*#dNXq?r + aoij0&dV)T,0D_eTYr+Y>F%9sV`BkK6B%m1RM\Yu.n[H*9$/-rg@HKkY1kF`!a&m)n;\3_aHW*6qU!<>8 + Op+^J'9qQ#oUAUe8k:)bg]@`PYSMWaLn=r4Lo5r/CR$Me0\_V79nD4C2-1a0rEaZ7@` + (#XP5*JgbXh1c.lW"lsEK`H;4i4Vqg9$/E92^j,3jQic?V9[Q + W7bd?#l)I5LQHoq>^EZheFE>2bFO*lj5A6]SI!@l7GoPgj5HK2r\\^SRW]!dfa7EsP6ib:B + !QZG182LFi6j0&;"fl]`2]RW40ZZMLMEn9N2foJ;LseiT%`n;Q\',FCi7pmT'1k>#3$]6K3 + 9EeS(Wh@I\9o2)oG^B7*(e9PakRq/llR?4+>*2H\Kmo[39O(%\C.uj!85+t6,n*C#j23uQbe.@m7-)Y3oZsAhd)?^H`?Ja01FkT + Jcco\A(K=I1HrsBQeO?7aQh9%O`8&n$f51;R'`4o"rQj525 + Q,rU"anZ1CJZ=,:bgBp6FO[H"ude5fL6(u<=--F$b(MC=G72dPn#ac&3&+$fYnHTeIoA+'Y + #][=ToRD'c/iG)*!6!fg==Wc'C?=*mrf2dI!@4'q3PcS"R0ZY#H_#l^1"e-<_-3=sKP(X.@ + DPUscZId_hcqo:nqfW7(XDdgN'@4/g>&XOBV?do3?cK<@k:8SIrZE.#*)ph#bb1/r( + ePjsdPL[p[cIIp#eXP72gY5Hqdacmse`5OV,N-j1f%(knegoh$CZ[IUg9ee5?-82A^#%&iD + I*cg:Ao#e>OaRmEn:0[f*q;ZZh:`KU";?#5F$B")uEF=GYc;(2I1W)m<]hDHn,=$=6",470 + D0O"P=Z4fI[>13SN+X]f/l5aMW6R/VpJqHFLhS\(\sVb0)aHJ]UiC/IdfD,DEXLRCJ6?dA< + )TZ]!5MQVl04dGj$"'d,Lf,?_u0g!q=XD'[g5-)!IKY.L/`bcD=R]?+Mb47bbhq`/R7@m75q. + _qY\n>T-^L.jRO]`4W0KeFgj^"irgna]0q8-C)b1'@T*6fL$V=XWnuQXWpX.`)R3l@!io>maC;X9BVD_1V2g7.d9[>]Bi&#!ute:b-C@g>SA=G + ][&?6SQ+Vu@q=&o^,Oi:0BtCH>gB0Ub&M04>homgj9g0IUg[P(XCOqSC>,o&Ud[l`O14?<"sg,hf`Qi?=m$BB0b=t=IoB:p&38IriE9Ki!g@X*>Ms3dHrOhd=qhHXp3 + "bfjKLRm=#CTFW\.6+uqR8kGAaBj-N)JR-i`BkNeW#r@)Cg/FF7M@Iq//qH;]Zo + W2B=IUkMc(l@Ke^M'*iEX,?(`j9BINSjM1-E5MLMtDZ.*kG*G[1(hm8Emm`Z'ogl_`9KZYU + @j[^N6cm:TbAX8M@r5(*oMn$arF;Fo]fr7BDPb=]!8RiD8Rn[k)93p\eet0;#[S<`-\+'?6:F0ppSl/ln>2Ef`??*LF + FhDUMZ0/m0bDB-Pt!%3+\P$"mm,D2k!*WB'2ji);Pk0^-K4R`Vc)W + TDE\s%>GgV$12)%mmWG[S?*@1LY\p$Ukf^6ph)f<84*7nXFNu8=*[oMZ.J3W0FU^b-2O<@c + ;f>[OF]L52[oVsO9m)Jf@p`DX[E/$\39i/&n)c]3Ais(B4YqQgFsYO9^!^jX60bN-lFgQib + [b[0fQNP5<_fm+^MDDBCLk1t?1hsh#.EUSiO)\FcQ2&\2S)s+'3?IQhKkpd>kr1'=C/`ZC^ + k[[V=*Hk)sf]gn_RX12]ljQJoVV"Ak+'3T8)!8$eTpOhmR4-[V=+fC;D7C=PkU\m1e4PdGQ + p$\7T^-NcC\h0NhI3D.T%#FJ!E(+)R?$dLS4X5205sgiJ1AF-NV?jX9lSUJdYYD@REF+Fl\ + p5R7(=#U"^(i.:)7FrLPo5m)'ke.=DOJd*?KGT^/q'nQT70L2->,m4Li!YVFS4pVXM"(6Z# + +>.F4"[N/C5XZ.o5(Wg.J63p,#Qpj89Jj3Y(aA,DlccJi + GP?7f3k3"E)$pROBCu#hjkg@#]se?X!YK5oQssU_0N5Ji'-[(aD?QcelPVcphg`R07*;)-j + *b-4%\E1#S".S.UY^@R1iM7MHGehL[F(MteLm#efiG(Q_)@&Tn4]_&"1A,2NU6Jh:Ag:YZO + oJegm#@Y,@,+p_8CKlRH"TS6h\D#Wn]3[0 + hKqW$(;U%VR!GJdsVh#WNrZH3cGPK5EAq%>Z.XJdk6ndnq6?63oB?K/*]fTS"OoJdc<8`.A + CG+Mh8)K),a++G/ka/g,c%Jq=A$+L6S,#U$<^+G-m).\MBf#UuEuQ)]EC-m517$0WM&D]jX + H#UU[G=pQEZkTH%?#m_Z6JdBMAjF;uA+GPIR80EP5+G'Y#+/"2U#TfXijpH\FBHVDK#:L^7 + LEM/Z#TFn;WX'LcJY"WK#"TkGJd)K'?]cI^EX3GRJRSm"j_,c9Jd!PF:kAi6+AJ=JJLUp<+ + FtF:JY5OX%"FrUq@nFQL\`$f+pa>[N$%8orf&#`iVnl[klLdGCM=tdPU2+]R!SL99qrh#hJ&cSLHl7r:6De`1;V^`%O"K8?C_>^PYlJ_`GEGGWoZ7Q=Xc@;rdqUg + ;"a84_DY$BuM].XQNrk4rinc$o\_ + s_P]0BSffUXXr'/Zos%igr5nEVo5L_N?;$naA.-2^=e;4-&KDeFRoe8V[e)rJ#]4AWsAL(PUaLP.n+DAe[YW\6iC=%omB!T!<6/!a^Qf4OL#2gb9-%0J#rbN["nM- + +7!*=XbF-k(BFJ>j8Es.ouKAL0`PDl8TcZ)gq4Gj73IN\9M0#ks/)B\s/-q'g,SKsJ.1dbfgtS$9D[ksF2o:kFb%Uf(mL@"B;R0?Gqi)]ZKR]:Br`NKiO`IU1bdk2Dk=_cR0&ulB\i3% + ASO9E"Nk*aYOoAh1q2KQXc.fjsG4/`6^GDY_c]@ZanQmH]Gu$0X4U7Wr8,9J5k1/c\=0hPV + led'P%"FdErc-!SaaDF"oY.fgqql\N+6R0aS[&0NG#G:PcJ4KGDob80ZFgD5LUJL9V_PB`e_qZj(rXo0uZ + 27o^4rmaT#%Ui]G2jq]Fl-JPdZj7?@*[5Lb(:ORb"naS@&+<9X?\j8DfrGqCA=jIVSKMGYl + 9riF0:[?VArsd+o];*9^&%2\p.""jMZiN'Hc[DJ$Fk%4t#FjTG.`a11C1)# + Q$!E'HVtTg)i^rSJ,X'rLOPnI*cuQ6YNSBUpRRs880(jC/%HmSZFnWS_B8p;Y8ZO`;7"Z;Jk:lHP>pFbU` + peM4aOrm(?Q5L51Z$*;23/8;u)5C/P&BFb2]_A9K`1,DAdTfZlpW!$d>ON>:c2((IIPDB1s + +#&0"cY77"=Ed(*/FM&_>$M,a-cIPPT#==6iRVSgcpU*9Sp]F>trEas,(f&/X$fW(b1eOX5 + t2u`n9;l8srV=$EmTS9OTNM@BTO\!hBT3`nBMOP!9[TA-63!bQY*Wf + 2"&2Y4O[leU:8Ek:;A$T9eoaD5C\5TMlLJ;`>?8).9u0R@ehJ3UIC + DM5uoJ^X^hod?o9.kUM2[->mI?"lG-fhY4kd<*\i[=N\CL2NaAq5eX#OXR&N]aeL,e,6NjJ + 3FLkW7fUP_OQfErMMp\UbOH$ZUl3 + Ns^W=L38Belrkj,2&lg,IKN$+/).1`T.O3Z7m8I35U%)c!OmV*[/XAG(==H^u)<:9[Jmf:A + ,9#iAUnE"i`.:D[^KU0i@QOb?EmjOPV/BR.:b%[%1V8i,q6Ha)EbNZ8(%o1?PBCum8EjS?>Ks]*:7-N%:n%Neu+8^W"It>B(FQWk,ARcro0q-lCMuFUAq6nEXK,q/Y7"mU= + sPpX@GUYdM1G+;VR2E!UANb9'7C8b'-S*h(UOdLPE#G38On+hG*5;.h[/d)^MQZH]@n+.k5 + p^146533URC90P%P'k-X=:iWI3[:+6E`@Xr6/J?CdomQ0CtLpuRp\8m\la>W,i<=,^nKSuN + R/n0NpB'->!]-qF+ZF&eNHKWS)$gZ#mbLJ.]Neu&F%-uu3X6b`3U.[,9lEK3^#g0r=cKg/; + ld!Z(@9VBbhem$BOO\QpCVr%.I + >B[nFu'-pb.C?/InGu(HJ[`U!K,%dA53J0%1E6BTK7DW]\cC]$t1EqpE:Y&]SV6b5@CIr5Q + tZ$?4R5D&r"a-njO#Drbiam/F9Zrt4nUdJ2B"*?XbM+>Tokqp&"<> + .Wqp=])XTN]#+HQm[iLs_13Z5@0*fcZ5-fkJ;Mdq>70qrb#r7hREghYZT* + b(`$7Jm]CT?NWD_!G^7I[c:Ye)FLM(C?u/9&UcONP9/`p'_Wl3+:Bd+p;gf]s+2lX\U"nF& + "elKiKbm)8oX&HORbr9_l=mNj580bZ9a%du+l<3[T%;'duS8M\[F1D)=+OYk,i(@fj=ng0b + >;b7[e9UO:+'baR^8Y$TT[8d`/(,&H!F)&qf=S;9-p.XR,d=!thFSBd0pVo3QnW,[fLA9/` + A^R&0&YV6X.lrfl3n9ia&[;-%[9@E6qied!'>n$K;d5I1>K4ci$?#fBaO]h94r)O18uDX5R&a6TX0;t=D63gq'4;Lj?qn=BPTaGTD.*e?!qmJeO>fO-,=@bDhNfZ[2D + 0=l19rM.i"B/MCH5oU52ctf-E\>r<`6?.-K6Va/f05m + L%K9%o!N`ui)SS0jRBPqU![&[m(F(m/B$eCd1E5bK + DV+`eGl#>#6db/Cmdqa'VlHnPV1@&a5j_T&&lnJ(uP5T5SpMG$Km?%V?o+,^r$#XkomdV._ + 9OD]hAJ01_n<#A%G6LmR#k=5j;caNsG8//]96&98'fAR'F\Qn[72>8a2K)ModqolQkB$6?N + qI=4F`R42K(kIeFZ"_mFbKOPPkaV`FtFdS2er1DX*6-S)_F3se%:d0Kp'p)ePF38WbH`Ueu + (Hr4j&DAOm-/>;0r%>?7M]&=^QO/AUG6bg5lfulHbep*3GWU@&iI>dOq^^j$O1VR5.%3fpQ + S\J5B + OYQ:IEdA"0i>S*O4bH,iEXb=B+g[p*@5FGHEmPF_93\N/bCRs1IV77pkaRX+\74])B2#gN%@`o(RNYD:,u,@,_)n]m'^_Ypo:q6t]6Zb=qF+4a8Ar-XV%jT=;^&!$ + -H;K];ENL-Ge$`@)\^O@^,X*0QQ#H-:@ii$W*%XC387G8hXqibF,sOHeRrt9H,-m\Wg.e.e + U=J-"i6Y<=IQo`g;,g%,HQ$W_UY?^'=X-+J]$3r`"fba3Al(E9+4(U92BZ.sstr.cJc-"1",aVGi;;EM3!40r1a6l_sIgHB%lVaV + K(7K\?7KH\Y7 + As.FUDcs)^-'Zm%Jc_69k\c!=[eDG']VkU\Mlkk@MpkKC7[;DHjD:J`+)o&2YRdSh#;)k;6 + 4\9R?9t_>@]i-`#PqB]*>>m[l1gB']S*ALj+4enB^D0PC[8X$s=fEssNa)Tc>Qn0>T['&:f + b[@a[o'tFZ]G]QOB%4#MA=ihBA1!GG2+Rr4gT@$dulSfP&\7So>SX/iB4^5*?+0I>4'9DbJ + d?=Y>aW?[G-s,<,gMn5F8k9S8BBq%Gs0_hj+2C3u_RTKpVPgaL`fdglm24Oia(K@[s-[fD3 + R95N9+5792X[H*Mn&'7f^dJM;#M<`dLg>=Xud[JA4%DEi`[M1XqEc.7$_)f]N&FXgU@8iqI + HoVp.l\%#J)+:4,3rWN6p5go-YZjeMoJYaK'3#YBnOc$s&&2\bD*u9)R]-b1u"[8L`)cgAq?@XAkU>V`re%]bV1gW$3jjgQ_7pB)0,pc`k + ]bo1<^03""WK+AS;r^##p/+W#T-4#KBO.7JV12<"qDb`g'/!/@*Cle + K*W;HJV(r[.Y326=p>;+@$1^"TO(`7gTEV]kC+CIeG"<_)7OF_r'(^bhEL + _p@FTO8B:!4Tfos"-lajL>aCkOJh+TP*6gIE]bMoRWV_)=W3Ko6LUf+D7\YH6?jh#'j!8)< + -4=#T;KMS^\P^G+`JW#'HSO;?h6ja;N?USELj7JXj*Y#'(i!)?t1(B*ni^<(In@JXa^u\q- + ?9=pAi:B$pm"TOu%FCY$QEXFZh=)5r+TFnu;V+D-K8BHUo8OF`h@)4H,EL`)M0R9#a\A>!O + $#%O<<27S4)W#:DBQtR@jJXAAW#%/Qbq?C:LAaSE,Q\ZN&JX8nFEe0Png'1cSA[2MDTOju+ + JX0seA:^$p).A8mAU4P^+D#9lkPBRUW$hFrJ#K:ANdn#ig6SV8lC%XN+6oj)X.S\!riMgXmhi + 70.iQ]_XN11cJ$!A[g4-7&g>]ZGX1J"SY=JHcJ$8&4!e7G&>7/9Q/6a0FZ,c_cJ$N_a.Y"c + &gG7$[XSYJ:Zq(!cJ$eD9;Lc*'>?]Xe/ZJa'+76W"`kg + gfMFE3Dhl9QNV!.Ue!rfK_I7j$tB-D\#;PL>iu8jiP,O7]UL'$=L^LUqQV.-b^ZZjriP9S\QQLor(++\GobXrG@Vg].t/uiEU/`FH?+A5M'$;?).@BO7hNk(Rk#'lfESVp%Uc'q + #=_HrIL%'$T]+d$"7X4J$@o2`(0\<+7)sVZ#Tc_B?M6(XF64lp-_-u.0!i + &SCRBpo4c%l0V+7/?DZdJcua?bkj"8n]#np&qoK62"UiOq1ipeqFn:*8R`_)N5\q>q>_W$$4=U?0a=<3*eVO!J"\%bKa7GiKWpUR6E"#pTl + MD#ka6joJc:P#X>G\Dl5i=VI#\I.3ZmSTJ"u4c&L'V8M30hH,f0JDL\ai)O\RFGOGan6l4M)$E6c9KcSs%S.E2b(m?)rlluNj7G&Vr@WJ#Ps%5Z1:.N.1%/n7#4`.rCRUn/L.'-Ocj` + c9?ESWWJC>K+6ok%alu5C,rDQc;Q3q4olr^OoWXVOcD1hhZe,"[OS!?AiKc$XFdJm+EAPR2^%]GYEZr`c:s/Pa(XgpqN=,U + L+F//`>elpC'>pH/IL&f(OdJmHeSTV+'dFqZs-tXL`s0),#_:VhFnLQu)230SDf\7KY?CTE + LL?RJ6#J<:feXWtlOn)ZhS9,79miV7peCqCsrG?Q64GTX2&,U'XXi(Eof'N8UW#e1k"i@d9 + )@JiHP(Jd;+!:5;B;C7f'RWbMP>+M_ZRU4T[RpX_L]3.%OBho[j5].M,Tki6NYNec1 + WNHJ9'lB&l[T-@K'ktcJLp].1C9%rmX"hu%:;MS)34F14RNeK:9;%g.\2j,5LX!A1ka39^2uqi + IrV#d,'PXumph8e&=7k+N.GTc345H^Z*^RkED3Y50$>*%X,H*P)UMU'gUg)Yh2XV'9-@0=i + R8g*sV$.ls`3'(IY01,6"#Xs6RE,?qC9UTpShZEUo'0\S]P@731t6G%j_,[1s.CV,hk]VMm + IZ&2:2W_Y_h=?Id+]JCWUCEY!N`g.ji]Se'^j!Q/E.7G3"VDHK&_IbR_B/9Zk4X+Z)1G^B< + 2(%oR7B>%<1YB%.Fu.!Z4i)tulGmuQU2ma*sETesam5!1>ED/ai1Q7mTVI__6^9Z + B-*DP+/t*D5R1;6XS&1ofTp"o=URr-NTK3c>Y?E)&H6PCA,!&r*__V.aP5`WtWZoqN_f@FU + &i([g3NusM0SI&"&ii#uZU\%@ScYqed'<5^l88itM96us6AlPn'+_5/[!] + KAim!`q-&brg#N4G6M9@U??l?bF3k4O_SE6S0X#0.)%nQXeIW4BSpVDR3ej]lAQcK@r]B4" + "f,:$^T4X51JU@C-r&1d'e^,(i'8j@3WM.!nQ]Hfq?>keK:5Z$3U%CKD(!*b"Ce>"K"M%Va>d0+\\jfdMPTX. + n[H*9$/-rg@QQlYZ'*ns6[V<_"*1M.?-I47l$7[`;VG7r.iLI@P\6^\!#k!R&7kYg=>*5W`h@jFoj(L/;B"^k6\:KSdL?MU8DJ5g+D3, + `Y.VO&5Fq_%>?e,a,u#PFh]:_,Q]9@Iq*7.\K + iB03,2,d + bh]%"8hK!6SL9tX_*c!%f^?C6CQc3.P# + &q3S$@a6f?W$Go\A)k:&k<+IB/F>LLcs'!SGg>/o + !KXe6RY03lM>9,cZ/N^R%4PNk!U]!B]$pmo$\jSuu<&M9e6bZN"^!pLfe8lYK[7NJ[9jB8C + gc\?DPYaKDdG/S8>^]I3lr:Y$em,=Vgq?_$6s2ZXg>)'u>l@hhS6`h74l%VQ7MXFe"CkIAf + 2`pE5"4.*7G9/\gGHi`?.+eJ^#)T=Pg8ic56UL6C[tNuo@F-;[B0&Q/G>R'pSp%F:Rbe-[< + Ks9GgFiMcfKLLn]K2[YVr7la>7Z`?%AtsDmpF9[jo&C9"m5_G0%RG/3Sfj;gPpkOLO&(d2e + 1n.tH#1O&8L>d9=r6Kbq7H)HfKI=]HD#]c:1'*?[Kd/aJKY*25GKjB9pm\QbCmCRD94Te=N + X3R"Naipp8No=>=(9)GBeQ)QF;VE0+33gHdF7BKKeX@n4,>6Z9*NNt764ki?2_u2c'D*M6O + \D*!/gR:Z;Wu"0@]Y")4e8;s"ID@s._,R8J-'cA'6c_B[d(H\6giFu.gWrSmd'F2)1F(@39 + \I*h2r+!b4cA#gj4iB9?!TOaRkRpK'9.Ga.4@\MmgGcF57=)+[?b^CtCmSE,Njj\k5k$MIMdcLrB==.M7ESBPC$J8 + s&?a8VQHk0>VC#AW"?j2P"'GS&,ct;p:XY+SG1=kB*S1U$4dE:9XCnTIaGi79`kG0D,mX/F + ]'Be."g+Im6=rZZ>/m<.F@@AG)9JY>)L>h!F@I:2\SZcK:3]d8!@QocU40uij[4q;%bSq/R + Q+8VUPY>R(BjF04`\1#d*od^GZ7_Wjou-+O25V.]76S`W(-/7XU;_i1l:sL?(8aOFd-??Ol + CL>u6f1?Xi3YtUc;R3tp"?^T8^?pL=ki + H@N2@K@>-GSZ-m8och5O2tK/b!?+C?=gajno8JAV&`E#2a8'?[BEPSb^X?qU!LaXA7!sh%/ + E0Ol-)#/B.]f7a^-PDp`W\_39[l:3,9@Q8lf'\DGiqY'2l9GZ##s69a.,jq(Ec&Q33R0#lc + ,74DYF.cFk.DB9WmhAK5b)D@uKiPdQ\e?r`F!D@=o;g#:JIM]E\"BIP93/`'hM^mrgJN_oV)m:2/ceDlq)jKi2EG;raW-n)c`4DEV!S4 + u86#A-H1J']3FHi3W\Uec,ha?ERV;1RK9AT$Cet0:hk7:>n&8Qshl]cGUV/ + Yb0'A"TGa*,-lNkTLTqJh18lihZN(E1o/)or6$:_iNI-1]DGo;ccKneHc'I=pmGph!0Ak$8 + AWC^oaMM=Q@Xd10omY:@pXd,66go%)7B-SK*R#4i>TdD%HpSp#0Ef\j^k@+?FYioP$l38*s + Ng'6Bl/q,gpIo;kg6I+OnB0Y*m0%u'?iN@kqDMD(BLEa2r@J<7N$O#H03FXX5m^Gg;`pII5 + -Zmr.>DH!`VN^Z_nl/C_l#&-H7eL!snpUZP0be[S1l3hq"pfWF$1ef!sfNjRFS.cW + :feIT:Ao^"6U-jb<&@QGCi()d`5Cn]o&HrI3*NJ*^J5AeT,q,mS$=$#Gl\nkFkc`JU$?ZpNH2JS=ROR.B$sOodrOgmEr;BnS^# + j[('TT5JR&^^`&_&Ufl/V48hJiM\K-mOHPi3gB?n4*$^]**"W+H]CmEGTtiI[=TtjYXP&oAHZ,O(*.'4^'Lln.q"7U,-Sle\3JeA>4*eG!F+W>qEOYR22TS-2K7%f3$?@iaP+Uir6W$)Fu&96,(5p1gf#XBMbr.]e6a + <:>2%u;A%Je(b"#X"c4_?`5rK=%e5%]CN6JduH:jFRjt&dUH>K6de^+G4L]JdmMYeq+?%+O + `@oK0fi"TS#!*18'7FrdkPl+N/j?-m6>o+G0Fq0-Gl3#VI6Oh'R^]80G3i$O@lY:*>.o#V) + L!U'N3@JsB)_$7I$iJdLW`SQI"dC'Z./Jm&F+="#2SJdD]*Ne.PY"c.YaL`hG(#Y6(jAfu[ + ,#To^jnd$;0J`X%d#A>6%Jd3\HEH!%O\d05tJZ8to+G$*7Jd+ag@Y+di+CBF;JT;#3TRgPX + (#Pr4<.Y9%+AdXmkTETQ+Fu!J&g2`D#SCMo+@:Y_#Tu<_"3;THNZ`l3#S!GWdKg9BW$!dG! + pCaXJc_kn.@;6.RKs3BJ;OUUtXNnPU/ir=F[0fqI-ZV8YU$l+6goHSfd4erh(@u&cSMBW\T[4.ED:RJ#WbMUjoD"qQ, + 1I*&=n(/'AP(Yb15)rj*^462n86lXqiZ/dmj/J$,^Gn:.W3qY5QDa85ne0ME]"`?4G0h;?#]"mWZt?3_lHSJ%Fh?["#]Cqn.Ek#Q)L<4HD;Fp)iJ!J"GWuHjBK<+6V"#T4ejd& + (1;?"8r1[oE7D*+TF^Er<*Z''aBb/%RLFtJ"\UrKnoLA+6[BfTu^,eOTH^pLnK6lEW1iNqC + E`P\::7ZEf?-T2Fe&BJ"s:JO,*Qs+6a&\Ue$Z2P!Vc4M*QQdnc"c.qG\R$=F[0'ZBg[5?;( + ZeJ#4t"R>:WP+6f_RVT@2TPCdgM_u2WWo^G)od/QmorBChc!?RIJ#JM/UBgXU+6l+ + @W?M8;,[-L4$>E9Hof>=f!<6"crDF1!9L%&qS5i1d=">HnX@N,o+6qL.X+C8QV-:N5b5l$& + :l7OS3<*,+rFM&_RkC"Qd9:GM.8O7gCGQQ"sjl_Sccp"2L\GlM(prHXIsl + Sb*@q-Rq8J$7i1^dn8(+7'\kY]9ZZB3PaPX?DE$p*;lTW;g>erJZg22F#j\r+#)IOg9!+ab + TaB+7-(YZI/Zpa87TbXNd3?G'6+'d/RaRrL]/q#XOn(6YPj5V6ngB70iW(+66k0[3i!+)Uh + r,D0V>/dULmbq(j,Re:3[*f%QU7E,JCt]/O2Z5KZOs.;eUg\+]s^gae/!j_TF3dJd5&CQ*9 + D8]j&+0]iafs"5Po\[glakZo.(FRH!TQneSK8,7p_m^_"47r7T6_3H,)>^r0(rat!`^3EEc + j#i*YC.J*#+6Kq[ReNEFSs.ZMcXH/ZdJe()q8"09%I_!n`;(K2k^<\(J"8=nF^K4WIP8tB^ + N]8J:FQ6*j8APap\b)b,R/f2.KpoVrdMo`rIPSE$,K^s#%64*6)4A`O7XY&@0@j/d%V7AUc + q#j#ld;&r=[!b@1=Be3/:>p)3=E)5LI"@7kk2j6qE:M,#h0*j8C6aoTh_iCf-'%;Cb1l[2+ + qXs%B-c09>bC(h3\ZN7"@W7\nm*dtn$!EPaZT39qUFb^5Cc%"G3Qrh8B]J<5p46qF9p,8@Z + f;^;68aXJG\AA"=oEct3XZ2UdoqQH-(MGm-(<@]A)]cZP;J#j7]YhI7=QEo@PbIMf'#Pujj + lVN)5Bn\6Q/B=/7/nM1Orjdg\kTB,jBh]JG2P^CCt$ + IK@$m#gtfZ+s(&4-QEq*Qn6*)`F5KOgZkePI553rKcKX4KDs2])gL"c)"9>lc`;-$M(-h3: + ]XQ6W#i$+$J^@XA+86i;gpfcYY&YCIYcN?140*MlbVFoSE>G;dG!GF)j1Mjm+[dj1OA[W&+ + 8:36*!aT$utUO+[H@@B;I3U_Y%Ll: + "=ddJo&C4t2Y8:DPE;8ucFks2J0i)bN[nN`E[&Q)"@$A.40S-([P-b9:FJl$,G`AkpJd8n# + q+Wje?;'!i&,ahDZXi'c=hTD@AedBtrcQN^k'>98pr40"*hU@M6"it + %5q"(:AW40f;"=R\oT#-0Zr]mW$9$9'faQ?@IZ_#F:=Q[#*n:%a<>(a0fVtIGD%u*>CI3BM + S"@-g:$/30]m`q($\[/T4iLD[)7#/Qb]9hUX+rU7#$pGcfc*B9odbR^$fqhWLor.jB=U.XS3P"V(Z_XpDAsf!K)DiFB=TVsNC.=FZ6:J' + KT.mP'1Z1q+kO\#:A/,R3=C0]]9nK?KL4%Tl'c>ilEiKr_;/la7([E/*e&]EihrLjm#K]'* + gfkc4fq@oaK-8bgr9p?=6Q[Dj&Mj,19(1q + ?D?D7YIPAWmc]ee'G=qVP9nA]%"a*eITG@D,tI"`b_j&VWQ=)ERr^-7?:ga?jlE$VULGctb-h.&e-Xks + 4r$@.OBH-P\6Xmk4E8?um^`F:)JG?*9%NG[E&-P;Kh'`$KE`t#O?P + abC:q#7"W]TjNs"!#X5e^RcUJCjm\qSLg7Ot@-6NNk:Urd/&[4db_Qa^8+)2sd)Cs!i9jGN`5s.@;\ngN0 + ^ioV&aX+o/8m;)#aI%P''t(5%#1'>B^uIKnkhZoWIDu=K(DF@o8c$L/@Lu5^7$]I?4Tg(0k + ANX^6/jEZ5%B*s'Dp!Xe.8;C_g30*9QWG]G^H8!P=G4!YA%,Y+aXM]d@E + :B]*E"VOj`Ea#fieKti7-;\0#:hI+1*o^(>ihP65Ed\1uJ:Wl;#Vq5BBIO4fLub^9.?7FqY + a[*RSqW?*$cI4I"fMQVl0X1pP0k,cmeH8B-LaH:&sFs]a^$]"1Gb + .P1WaJIb`nI#7help'U):Zdo+?qRl-=02:5Vd$*?*ZbaYqS(6`YPaibJ/jS!M/2t(crOb#K + 3KfH(33:Vtg0p&'uPgPPTq3.QKk'7kDWm`F\4"kULtik73o64hlol6"-4ai=!c@$i=KBY0P)T8BdW$;-STNfE--VLGd + d#sj0FF#"9SpmNsO0sKjHL;97:mc + Wfqheoeqpe]BuaF$H65C"s:! + 3TTd3BDE6T,rPD;bSVk9Mr76rkm-NV&P'i9jY-:tFinQL)T<*H+^S&lg)nod+Q1 + &JGZk1Tqm!;aD)i3qCZfo-Leabc^Q@5rGi8bZ?@>Y>Fn5D"ZC?#*A]H7V,aarZc@lr_ + r,gPODf%M-^kk?:Q)DMR*9tnCg\F5h5`t(&lrg"$s@"FuB]1D8tAZ/DVfS@B0Icc/U&9kcp + F'Z0h,>P#1'%2.N[Ji/S

kLs+orQtjXQ(*u6MBqt=*IA?[nuhn;:?!sFieqH>dXJYCdq27(K(># + 5\2?i.S"?p\BK>[;LhE?AZ"Z6"KntMWE5QEO%u,nZ3:`1uo(mHqR+E&h,E;PQR[E1P2`$KJQ + &dtoF:W.'O)WEj"#u8@OI'"q)RVT-)lpB2d^^LsEN8lG.6CZaXRj'+0ticJ_l^8g>I2_A'@ + i=Efp%WUW&[X;HM*PIWBVtls"0A;(U]Qomb/3Tg3X9q=3!U$[fL?ZZ'Wq,D980P/"#fZZ?@ + ==PWO?D6%97_ZWHrjfDS,t!4/.\J.o9>L,?l(FN]E>o=7tE9mCkVfQt@1TnkPiLiW0EZMMGj + O\`TsM4-e.DW_T!mCX?5MRN*Ed3.p?c&6mUnG6]og1^$2fl7NMEE,5CO\Gr0n23XhcZjFq5XZ\5@aVtA2IecX&7'iD7(3"S@lnMQ=8#5f7Mf.[T&=4fK#O + nj0ojm8f1&jGY5T[IEtrZ96Q`5=C%S[NM+eATb,P\2R1XF[8"*?\_)>)::[*`l_;J^dWWsb + ]^t!kL*LBkkRlO^kFrVOV=(g6NVnuB7=r]+t9g;gF]i + rFV_mQkCgd-1riKQ4:D7iYL]Ml!+dB>:jYB06;]O=$PAKTY/?J>_)p%-3Z&$Y[>YrmG#S:M + k')drXM@5KQEHR4,$Phl/'6eS"5)`(t$V.2>Z@s^@h3O%GP[72%DIN5t1gqU5i;meB+[<-c + ']Zited3uV:B)?5c[WTjcS[S0]jq`YCrR0LNbimfY^ETSHVmdKjoG*2&')X:X=n2o,sM6YuT)Ck7LuO@%cl#[2fq@"jC[?Jbl95mjh_U_'F,.: + /[$(a23O:P'7aE'e@PR04hP$!>7l-4!.o&`A=U+Vud7,!cKR650u`TRm^tKD6Sc#X*bM2/k + k:&9S*q_$:oM+PmA^Jdl+?80H6.";sK00L:@'?c[g,TRtf=Z3VuE_$*Y$9Ft&('>Xh-a<6c + S,_?<[JhJYd6Ndrd"7SSJ&3KB5#9\T-+DM0"r4rIH5e74/+FpHt$:'Ci-@u:i+78)>+=BB-;qD#\G36=pS\>L`qq5)$hCGJf*%<#\'H]+p_VML(Xc"fE`XNJf!V + oI"E0C8dJ`GL"Zf<+GIrD>Zuq,DLrYF+_lTEQKe`YTS8O7=J1A%b^pSn+^BU6W$,8p'mGN@ + + 7efU?DA0J"qeq4Fa.3qF.N^M)%nn+6cr-QQPICrg=kmU&LNuM8FA+-jTTtJ#Gm6LjuADqN8 + Ij8n+LZ2VskN70c41e7F0J$b".9RjGTqc&'QSnhT`2GLIUg7RcK + rnS[`iVq^VmK^D`30#eZJ%6s(R")Zeqk/GM?YcV+3tB`PJ%MWU^jj!eqoF9"hs'>f8$M_0+ + oZjZq=GclfRKNErdLMSr'/7'!?bhMJOfPB+6Y,&TY^V[T^!(T85Taos?s7i;[$1+`K%[aLVWQk9'#gJ + $-We]8-7P+7%'cY=7fl(Z>=#A,Ulfp&I>0&H?-jrI[kB&AjfE&0L7$J$C0r`g+(L1hQphRud8b5M=upB?^X?1cC6=g.P(QA41p01&a + UFMbn/J=faKX\CV$8s!6Y&PdVpUfipOPD/H\hPEGi>a8%l9D99CKL@j@BiCb9S1k1b[r`!, + AJoK!Y`AkmT=3(,F+6AH2Q<,EoS?nT(:3"Pq1&bHKq0*qBjjCXe9^3WsXEB7;J!cAt@bm6Q + G-9ZV]=3+T9\BA$U\rHN2h^^m6cs\83Th=NrbP+#Z[.[,lb/XnpKmaS3+MmGO7SD-?R+c_: + U0XXj/P>h\GnW8r9_7#kl)[Qr.!&foT/Up5L4$C4n'3g5_p-W+ES)XU\t.QoE%8(*#4'e@L + YWE4U7()s$C5o$BOg4$"5)-Ki#&k6KCa'd5_.p0[^iVG]FC)gc\-CFW@/"r/?Er1*;k!)%X + @AN/,h:+#i-r%u^.?LUo5^fE2.C"CsY-ORp9_-7^;aif&YI%Rrh##brQuN/)9d7ROKjUES6 + E&Wl9I6_tY1N(8^ppub+6R2HK>=,UR?1)tPT)%J30N/%`q*#RWP%K(C.L@R)=\H*QWb_D(2 + hF9t%Bc.AEA2Z2\:Q['HCEk"'PqoTD)MlIKjeQEDZKa1`.*-(6,\/_&.P_m_aCaS1A2=gZb + K]7lAS39!1/X.ScM?U$,9`#)L+[r%"8lUQ+bkr1(5C3L_&3Xj(6!tHN^cA2)%57IN.p7H(O + BL-$`u=GKuu.e)M:m\,\/KhOLfBhi2L.++-NgA>E1MA0SL&WiX>kf?P;,\/9H.P_ZnaA)) + uA1"d#1(u(W9Ht]6]f31q'$/-dn#cCO&Wi2G6;)ju3Y[O+Jhdr9+W1RR(6!o]1(g$B)$mr' + N.b7J7RAC=#_Z_3q!T;%kf>l]+Rk>B^IIGsM&?OtA0!^L:MgBY3?o[,eN0r7%LC-7oqZ+;]';$[W5pl?,\.h@OD + =.Ga>EUdA/Z&ABC5Z;V5#0UN.WL$mg^'M"sd^rHgcH(BZMk%,\._(.P_C1a=__5A/4L + gt/GUQ%((lX0o)<0LKk+^/;34cKre@eT+JasJO/DR[SJ_S&A.uXk1't\2)$J8tN.P4g$!uR + ."J9jrJjM3urrEu?Dhl^B`T[+>-fo`jRIblOch\_Rl$YdqomXC%q]BJeIeVat+,BB8 + 5KW_N2s:,]VtGMRg&$8Cn,=ELq0tWnrI; +Q +Q Q +showpage +%%Trailer +%%EOF diff --git a/testfiles/cli_tests/testcases/export-ps-level-3_expected.ps b/testfiles/cli_tests/testcases/export-ps-level-3_expected.ps new file mode 100644 index 0000000..3fa36c5 --- /dev/null +++ b/testfiles/cli_tests/testcases/export-ps-level-3_expected.ps @@ -0,0 +1,134 @@ +%!PS-Adobe-3.0 +%%Creator: cairo 1.15.10 (http://cairographics.org) +%%CreationDate: Tue Apr 7 23:58:53 2020 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 3 +%%DocumentMedia: 53x53mm 150 150 0 () () +%%BoundingBox: 37 37 113 113 +%%EndComments +%%BeginProlog +/languagelevel where +{ pop languagelevel } { 1 } ifelse +3 lt { /Helvetica findfont 12 scalefont setfont 50 500 moveto + (This print job requires a PostScript Language Level 3 printer.) show + showpage quit } if +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +/cairo_set_page_size { + % Change paper size, but only if different from previous paper size otherwise + % duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size + % so we use the same when checking if the size changes. + /setpagedevice where { + pop currentpagedevice + /PageSize known { + 2 copy + currentpagedevice /PageSize get aload pop + exch 4 1 roll + sub abs 5 gt + 3 1 roll + sub abs 5 gt + or + } { + true + } ifelse + { + 2 array astore + 2 dict begin + /PageSize exch def + /ImagingBBox null def + currentdict end + setpagedevice + } { + pop pop + } ifelse + } { + pop + } ifelse +} def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageMedia: 53x53mm +%%PageBoundingBox: 37 37 113 113 +150 150 cairo_set_page_size +%%EndPageSetup +q 37 37 76 76 rectclip +1 0 0 -1 0 150 cm q +q +37.5 37.5 75 75 re W n +[0.75 0 0 0.75 0 0] concat +/CairoFunction + << /FunctionType 2 + /Domain [ 0 1 ] + /C0 [ 1 1 0 ] + /C1 [ 1 0 0 ] + /N 1 + >> +def + << /ShadingType 2 + /ColorSpace /DeviceRGB + /Coords [ 50 50 150 50 ] + /Extend [ true true ] + /Function CairoFunction + >> +shfill +Q +Q Q +showpage +%%Trailer +%%EOF diff --git a/testfiles/cli_tests/testcases/export-text-paintorder.svg b/testfiles/cli_tests/testcases/export-text-paintorder.svg new file mode 100644 index 0000000..405ed1d --- /dev/null +++ b/testfiles/cli_tests/testcases/export-text-paintorder.svg @@ -0,0 +1,31 @@ + + + + + 1.ΠΠΠ + 2.ΠΠΠ + 3.ΠΠΠ + 4.ΠΠΠ + 5.ΠΠΠ + 6.ΠΠΠ + 7.ΠΠΠ + + diff --git a/testfiles/cli_tests/testcases/export-text-paintorder_expected.png b/testfiles/cli_tests/testcases/export-text-paintorder_expected.png new file mode 100644 index 0000000..15d87f9 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-text-paintorder_expected.png differ diff --git a/testfiles/cli_tests/testcases/export-width_expected.png b/testfiles/cli_tests/testcases/export-width_expected.png new file mode 100644 index 0000000..f05cecd Binary files /dev/null and b/testfiles/cli_tests/testcases/export-width_expected.png differ diff --git a/testfiles/cli_tests/testcases/export-with-filters-multipage.svg b/testfiles/cli_tests/testcases/export-with-filters-multipage.svg new file mode 100644 index 0000000..79e7eb4 --- /dev/null +++ b/testfiles/cli_tests/testcases/export-with-filters-multipage.svg @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + This is a digit 2 placed on page 2 with a Gaussian blur filter applied to it. + As reported in https://gitlab.com/inkscape/inkscape/-/issues/3214, objects with filters + were ignored on PDF export when placed on second and subsequent pages. This test ensures + that the blurred digit 2 is exported correctly. + + + + diff --git a/testfiles/cli_tests/testcases/export-with-filters-multipage_expected.png b/testfiles/cli_tests/testcases/export-with-filters-multipage_expected.png new file mode 100644 index 0000000..8055716 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-with-filters-multipage_expected.png differ diff --git a/testfiles/cli_tests/testcases/export-with-filters_expected.emf b/testfiles/cli_tests/testcases/export-with-filters_expected.emf new file mode 100644 index 0000000..ef5d7d9 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-with-filters_expected.emf differ diff --git a/testfiles/cli_tests/testcases/export-with-filters_expected.eps b/testfiles/cli_tests/testcases/export-with-filters_expected.eps new file mode 100644 index 0000000..a1559b3 --- /dev/null +++ b/testfiles/cli_tests/testcases/export-with-filters_expected.eps @@ -0,0 +1,122 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: cairo 1.15.10 (http://cairographics.org) +%%CreationDate: Fri Apr 10 23:27:41 2020 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 3 +%%BoundingBox: 0 0 150 75 +%%EndComments +%%BeginProlog +50 dict begin +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageBoundingBox: 0 0 150 75 +%%EndPageSetup +q 0 0 150 75 rectclip +1 0 0 -1 0 75 cm q +1 0 0 rg +0 0 75 75 re f +q +0 0 150 75 re W n +[ 150 0 0 -75 0 75 ] concat +/cairo_ascii85_file currentfile /ASCII85Decode filter def +/DeviceRGB setcolorspace +<< + /ImageType 3 + /InterleaveType 2 + /DataDict << + /ImageType 1 + /Width 200 + /Height 100 + /Interpolate true + /BitsPerComponent 8 + /Decode [ 0 1 0 1 0 1 ] + /DataSource cairo_ascii85_file /FlateDecode filter + /ImageMatrix [ 200 0 0 -100 0 100 ] + >> + /MaskDict << + /ImageType 1 + /Width 200 + /Height 100 + /Interpolate true + /BitsPerComponent 1 + /Decode [ 1 0 ] + /ImageMatrix [ 200 0 0 -100 0 100 ] + >> +>> +image + Gb"0LYml4K!5lV0JGlC"dZfh?MWBs[8C#%o^E!2kgZRMR@eCIkJqo97!.!W7(I + DqoeQ3^t*\LK#E@?(A=FJ/^i8J/<'o.BrMq(lK$6Cg#?Z@K=\6+0uU#=m=ob + E76LPs\a*"!`>!.!W7(IDqoeQ3 + ^t*\LK#E@?(A=FJ/^i8J/<'o.BrMq(lK$6Cg#?Z@K=\6+0uU#=m=obE76LPs\a*"!`>!.!W7(IDqoeQ3^t*\LK#E@?(A=FJ/^ + i8J/<'o.BrMq(lK$6Cg#?Z@K=\6+0uU#=m=obE76LPs\a*"!`>!.!W7(IDqoeQ3^t*\LK#E@?(A=FJ/^i8J/<'o.BrMq(lK$6 + Cg#?Z@K=\6+0uU#=m=obE76LPs\a*"!`>!.!W + 7(IDqoeQ3^t*\LK#E@?(A=FJ/^i8J/<'o.BrMq(lK$6Cg#?Z@K=\6+0uU#=m + =obE76LPs\a*"!`>!.!W7(IDqo + eQ3^t*\LK#E@?(A=FJ/^i8J/<'o.BrMq(lK$6Cg#?Z@K=\6+0uU#=m=obE76LPs\a*"!`>< + aThpGdpk(`M)5K@eCIkJqo97!V?]6dS>#~> +Q +Q Q +showpage +%%Trailer +end +%%EOF diff --git a/testfiles/cli_tests/testcases/export-with-filters_expected.pdf b/testfiles/cli_tests/testcases/export-with-filters_expected.pdf new file mode 100644 index 0000000..c3df07b Binary files /dev/null and b/testfiles/cli_tests/testcases/export-with-filters_expected.pdf differ diff --git a/testfiles/cli_tests/testcases/export-with-filters_expected.png b/testfiles/cli_tests/testcases/export-with-filters_expected.png new file mode 100644 index 0000000..d988a05 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-with-filters_expected.png differ diff --git a/testfiles/cli_tests/testcases/export-with-filters_expected.ps b/testfiles/cli_tests/testcases/export-with-filters_expected.ps new file mode 100644 index 0000000..13f6215 --- /dev/null +++ b/testfiles/cli_tests/testcases/export-with-filters_expected.ps @@ -0,0 +1,159 @@ +%!PS-Adobe-3.0 +%%Creator: cairo 1.15.10 (http://cairographics.org) +%%CreationDate: Fri Apr 10 23:27:41 2020 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 3 +%%DocumentMedia: 53x26mm 150 75 0 () () +%%BoundingBox: 0 0 150 75 +%%EndComments +%%BeginProlog +/languagelevel where +{ pop languagelevel } { 1 } ifelse +3 lt { /Helvetica findfont 12 scalefont setfont 50 500 moveto + (This print job requires a PostScript Language Level 3 printer.) show + showpage quit } if +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +/cairo_set_page_size { + % Change paper size, but only if different from previous paper size otherwise + % duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size + % so we use the same when checking if the size changes. + /setpagedevice where { + pop currentpagedevice + /PageSize known { + 2 copy + currentpagedevice /PageSize get aload pop + exch 4 1 roll + sub abs 5 gt + 3 1 roll + sub abs 5 gt + or + } { + true + } ifelse + { + 2 array astore + 2 dict begin + /PageSize exch def + /ImagingBBox null def + currentdict end + setpagedevice + } { + pop pop + } ifelse + } { + pop + } ifelse +} def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageMedia: 53x26mm +%%PageBoundingBox: 0 0 150 75 +150 75 cairo_set_page_size +%%EndPageSetup +q 0 0 150 75 rectclip +1 0 0 -1 0 75 cm q +1 0 0 rg +0 0 75 75 re f +q +0 0 150 75 re W n +[ 150 0 0 -75 0 75 ] concat +/cairo_ascii85_file currentfile /ASCII85Decode filter def +/DeviceRGB setcolorspace +<< + /ImageType 3 + /InterleaveType 2 + /DataDict << + /ImageType 1 + /Width 200 + /Height 100 + /Interpolate true + /BitsPerComponent 8 + /Decode [ 0 1 0 1 0 1 ] + /DataSource cairo_ascii85_file /FlateDecode filter + /ImageMatrix [ 200 0 0 -100 0 100 ] + >> + /MaskDict << + /ImageType 1 + /Width 200 + /Height 100 + /Interpolate true + /BitsPerComponent 1 + /Decode [ 1 0 ] + /ImageMatrix [ 200 0 0 -100 0 100 ] + >> +>> +image + Gb"0LYml4K!5lV0JGlC"dZfh?MWBs[8C#%o^E!2kgZRMR@eCIkJqo97!.!W7(I + DqoeQ3^t*\LK#E@?(A=FJ/^i8J/<'o.BrMq(lK$6Cg#?Z@K=\6+0uU#=m=ob + E76LPs\a*"!`>!.!W7(IDqoeQ3 + ^t*\LK#E@?(A=FJ/^i8J/<'o.BrMq(lK$6Cg#?Z@K=\6+0uU#=m=obE76LPs\a*"!`>!.!W7(IDqoeQ3^t*\LK#E@?(A=FJ/^ + i8J/<'o.BrMq(lK$6Cg#?Z@K=\6+0uU#=m=obE76LPs\a*"!`>!.!W7(IDqoeQ3^t*\LK#E@?(A=FJ/^i8J/<'o.BrMq(lK$6 + Cg#?Z@K=\6+0uU#=m=obE76LPs\a*"!`>!.!W + 7(IDqoeQ3^t*\LK#E@?(A=FJ/^i8J/<'o.BrMq(lK$6Cg#?Z@K=\6+0uU#=m + =obE76LPs\a*"!`>!.!W7(IDqo + eQ3^t*\LK#E@?(A=FJ/^i8J/<'o.BrMq(lK$6Cg#?Z@K=\6+0uU#=m=obE76LPs\a*"!`>< + aThpGdpk(`M)5K@eCIkJqo97!V?]6dS>#~> +Q +Q Q +showpage +%%Trailer +%%EOF diff --git a/testfiles/cli_tests/testcases/export-with-filters_expected.svg b/testfiles/cli_tests/testcases/export-with-filters_expected.svg new file mode 100644 index 0000000..6b40fb2 --- /dev/null +++ b/testfiles/cli_tests/testcases/export-with-filters_expected.svg @@ -0,0 +1,68 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/testfiles/cli_tests/testcases/export-with-filters_expected.wmf b/testfiles/cli_tests/testcases/export-with-filters_expected.wmf new file mode 100644 index 0000000..830da28 Binary files /dev/null and b/testfiles/cli_tests/testcases/export-with-filters_expected.wmf differ diff --git a/testfiles/cli_tests/testcases/export_hints.svg b/testfiles/cli_tests/testcases/export_hints.svg new file mode 100644 index 0000000..87c06f5 --- /dev/null +++ b/testfiles/cli_tests/testcases/export_hints.svg @@ -0,0 +1,7 @@ + + + + + + diff --git a/testfiles/cli_tests/testcases/filter.svg b/testfiles/cli_tests/testcases/filter.svg new file mode 100644 index 0000000..6111da3 --- /dev/null +++ b/testfiles/cli_tests/testcases/filter.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/testfiles/cli_tests/testcases/gradient.svg b/testfiles/cli_tests/testcases/gradient.svg new file mode 100644 index 0000000..3f5ab8c --- /dev/null +++ b/testfiles/cli_tests/testcases/gradient.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/testfiles/cli_tests/testcases/lambda-background.svg b/testfiles/cli_tests/testcases/lambda-background.svg new file mode 100644 index 0000000..50b9fb2 --- /dev/null +++ b/testfiles/cli_tests/testcases/lambda-background.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/testfiles/cli_tests/testcases/lambda.svg b/testfiles/cli_tests/testcases/lambda.svg new file mode 100644 index 0000000..ef3d688 --- /dev/null +++ b/testfiles/cli_tests/testcases/lambda.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/testfiles/cli_tests/testcases/librevenge_formats/corel_draw.cdr b/testfiles/cli_tests/testcases/librevenge_formats/corel_draw.cdr new file mode 100644 index 0000000..ccdb02a Binary files /dev/null and b/testfiles/cli_tests/testcases/librevenge_formats/corel_draw.cdr differ diff --git a/testfiles/cli_tests/testcases/librevenge_formats/corel_draw2.cdr b/testfiles/cli_tests/testcases/librevenge_formats/corel_draw2.cdr new file mode 100644 index 0000000..4a74dbc Binary files /dev/null and b/testfiles/cli_tests/testcases/librevenge_formats/corel_draw2.cdr differ diff --git a/testfiles/cli_tests/testcases/librevenge_formats/corel_draw2_expected.png b/testfiles/cli_tests/testcases/librevenge_formats/corel_draw2_expected.png new file mode 100644 index 0000000..94c3166 Binary files /dev/null and b/testfiles/cli_tests/testcases/librevenge_formats/corel_draw2_expected.png differ diff --git a/testfiles/cli_tests/testcases/librevenge_formats/corel_draw_expected.png b/testfiles/cli_tests/testcases/librevenge_formats/corel_draw_expected.png new file mode 100644 index 0000000..b3ee3e4 Binary files /dev/null and b/testfiles/cli_tests/testcases/librevenge_formats/corel_draw_expected.png differ diff --git a/testfiles/cli_tests/testcases/librevenge_formats/visio.vsd b/testfiles/cli_tests/testcases/librevenge_formats/visio.vsd new file mode 100644 index 0000000..a526a70 Binary files /dev/null and b/testfiles/cli_tests/testcases/librevenge_formats/visio.vsd differ diff --git a/testfiles/cli_tests/testcases/librevenge_formats/visio.vsd_expected.png b/testfiles/cli_tests/testcases/librevenge_formats/visio.vsd_expected.png new file mode 100644 index 0000000..33543e1 Binary files /dev/null and b/testfiles/cli_tests/testcases/librevenge_formats/visio.vsd_expected.png differ diff --git a/testfiles/cli_tests/testcases/librevenge_formats/visio.vsdx b/testfiles/cli_tests/testcases/librevenge_formats/visio.vsdx new file mode 100644 index 0000000..ef8f6a1 Binary files /dev/null and b/testfiles/cli_tests/testcases/librevenge_formats/visio.vsdx differ diff --git a/testfiles/cli_tests/testcases/librevenge_formats/visio.vsdx_expected.png b/testfiles/cli_tests/testcases/librevenge_formats/visio.vsdx_expected.png new file mode 100644 index 0000000..21f2c31 Binary files /dev/null and b/testfiles/cli_tests/testcases/librevenge_formats/visio.vsdx_expected.png differ diff --git a/testfiles/cli_tests/testcases/librevenge_formats/word_perfect.wpg b/testfiles/cli_tests/testcases/librevenge_formats/word_perfect.wpg new file mode 100644 index 0000000..02e1b82 Binary files /dev/null and b/testfiles/cli_tests/testcases/librevenge_formats/word_perfect.wpg differ diff --git a/testfiles/cli_tests/testcases/librevenge_formats/word_perfect_expected.png b/testfiles/cli_tests/testcases/librevenge_formats/word_perfect_expected.png new file mode 100644 index 0000000..0255a12 Binary files /dev/null and b/testfiles/cli_tests/testcases/librevenge_formats/word_perfect_expected.png differ diff --git a/testfiles/cli_tests/testcases/multiline-anchoring.svg b/testfiles/cli_tests/testcases/multiline-anchoring.svg new file mode 100644 index 0000000..0af70d6 --- /dev/null +++ b/testfiles/cli_tests/testcases/multiline-anchoring.svg @@ -0,0 +1,29 @@ + + + + ۣۚۏۯۣۚۏۯ + updown + ۣۚۏۯۣۚۏۯ + leftright + ۣۚۏۯۣۚۏۯ + rightleft + + \ No newline at end of file diff --git a/testfiles/cli_tests/testcases/offset.svg b/testfiles/cli_tests/testcases/offset.svg new file mode 100644 index 0000000..caa96e4 --- /dev/null +++ b/testfiles/cli_tests/testcases/offset.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/testfiles/cli_tests/testcases/path.svg b/testfiles/cli_tests/testcases/path.svg new file mode 100644 index 0000000..57c27b4 --- /dev/null +++ b/testfiles/cli_tests/testcases/path.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + diff --git a/testfiles/cli_tests/testcases/pdf-mesh.pdf b/testfiles/cli_tests/testcases/pdf-mesh.pdf new file mode 100644 index 0000000..8d4c29c Binary files /dev/null and b/testfiles/cli_tests/testcases/pdf-mesh.pdf differ diff --git a/testfiles/cli_tests/testcases/pdf-pages.pdf b/testfiles/cli_tests/testcases/pdf-pages.pdf new file mode 100644 index 0000000..758c2b1 Binary files /dev/null and b/testfiles/cli_tests/testcases/pdf-pages.pdf differ diff --git a/testfiles/cli_tests/testcases/pdfinput/cms.pdf b/testfiles/cli_tests/testcases/pdfinput/cms.pdf new file mode 100644 index 0000000..a493e2a Binary files /dev/null and b/testfiles/cli_tests/testcases/pdfinput/cms.pdf differ diff --git a/testfiles/cli_tests/testcases/pdfinput/cms_expected.svg b/testfiles/cli_tests/testcases/pdfinput/cms_expected.svg new file mode 100644 index 0000000..2b0fcf2 --- /dev/null +++ b/testfiles/cli_tests/testcases/pdfinput/cms_expected.svg @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + diff --git a/testfiles/cli_tests/testcases/pdfinput/font-spacing.pdf b/testfiles/cli_tests/testcases/pdfinput/font-spacing.pdf new file mode 100644 index 0000000..78e13eb Binary files /dev/null and b/testfiles/cli_tests/testcases/pdfinput/font-spacing.pdf differ diff --git a/testfiles/cli_tests/testcases/pdfinput/font-spacing_expected.svg b/testfiles/cli_tests/testcases/pdfinput/font-spacing_expected.svg new file mode 100644 index 0000000..fae289f --- /dev/null +++ b/testfiles/cli_tests/testcases/pdfinput/font-spacing_expected.svg @@ -0,0 +1,189 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/testfiles/cli_tests/testcases/pdfinput/font-style.pdf b/testfiles/cli_tests/testcases/pdfinput/font-style.pdf new file mode 100644 index 0000000..06cd489 Binary files /dev/null and b/testfiles/cli_tests/testcases/pdfinput/font-style.pdf differ diff --git a/testfiles/cli_tests/testcases/pdfinput/font-style_expected.svg b/testfiles/cli_tests/testcases/pdfinput/font-style_expected.svg new file mode 100644 index 0000000..5f236da --- /dev/null +++ b/testfiles/cli_tests/testcases/pdfinput/font-style_expected.svg @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/testfiles/cli_tests/testcases/pdfinput/gradients.pdf b/testfiles/cli_tests/testcases/pdfinput/gradients.pdf new file mode 100644 index 0000000..17f09ec Binary files /dev/null and b/testfiles/cli_tests/testcases/pdfinput/gradients.pdf differ diff --git a/testfiles/cli_tests/testcases/pdfinput/gradients_expected.svg b/testfiles/cli_tests/testcases/pdfinput/gradients_expected.svg new file mode 100644 index 0000000..37d88d6 --- /dev/null +++ b/testfiles/cli_tests/testcases/pdfinput/gradients_expected.svg @@ -0,0 +1,188 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/testfiles/cli_tests/testcases/pdfinput/latex.pdf b/testfiles/cli_tests/testcases/pdfinput/latex.pdf new file mode 100644 index 0000000..339b847 Binary files /dev/null and b/testfiles/cli_tests/testcases/pdfinput/latex.pdf differ diff --git a/testfiles/cli_tests/testcases/pdfinput/latex_expected.svg b/testfiles/cli_tests/testcases/pdfinput/latex_expected.svg new file mode 100644 index 0000000..4fa185b --- /dev/null +++ b/testfiles/cli_tests/testcases/pdfinput/latex_expected.svg @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/testfiles/cli_tests/testcases/pdfinput/multi-page-sample.pdf b/testfiles/cli_tests/testcases/pdfinput/multi-page-sample.pdf new file mode 100644 index 0000000..6b8945c Binary files /dev/null and b/testfiles/cli_tests/testcases/pdfinput/multi-page-sample.pdf differ diff --git a/testfiles/cli_tests/testcases/pdfinput/multi-page-sample_expected.svg b/testfiles/cli_tests/testcases/pdfinput/multi-page-sample_expected.svg new file mode 100644 index 0000000..49fcf93 --- /dev/null +++ b/testfiles/cli_tests/testcases/pdfinput/multi-page-sample_expected.svg @@ -0,0 +1,1606 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/testfiles/cli_tests/testcases/pdfinput/rotated-gradients.pdf b/testfiles/cli_tests/testcases/pdfinput/rotated-gradients.pdf new file mode 100644 index 0000000..3fdea3d Binary files /dev/null and b/testfiles/cli_tests/testcases/pdfinput/rotated-gradients.pdf differ diff --git a/testfiles/cli_tests/testcases/pdfinput/rotated-gradients_expected.svg b/testfiles/cli_tests/testcases/pdfinput/rotated-gradients_expected.svg new file mode 100644 index 0000000..92a3f2c --- /dev/null +++ b/testfiles/cli_tests/testcases/pdfinput/rotated-gradients_expected.svg @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/testfiles/cli_tests/testcases/pdfinput/transparency-groups.pdf b/testfiles/cli_tests/testcases/pdfinput/transparency-groups.pdf new file mode 100644 index 0000000..977382a Binary files /dev/null and b/testfiles/cli_tests/testcases/pdfinput/transparency-groups.pdf differ diff --git a/testfiles/cli_tests/testcases/pdfinput/transparency-groups_expected.svg b/testfiles/cli_tests/testcases/pdfinput/transparency-groups_expected.svg new file mode 100644 index 0000000..c142fb8 --- /dev/null +++ b/testfiles/cli_tests/testcases/pdfinput/transparency-groups_expected.svg @@ -0,0 +1,142 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/testfiles/cli_tests/testcases/pyramids.svg b/testfiles/cli_tests/testcases/pyramids.svg new file mode 100644 index 0000000..30ac82a --- /dev/null +++ b/testfiles/cli_tests/testcases/pyramids.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/testfiles/cli_tests/testcases/rects.svg b/testfiles/cli_tests/testcases/rects.svg new file mode 100644 index 0000000..3d90ef1 --- /dev/null +++ b/testfiles/cli_tests/testcases/rects.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/testfiles/cli_tests/testcases/regression-1364.svg b/testfiles/cli_tests/testcases/regression-1364.svg new file mode 100644 index 0000000..d37767c --- /dev/null +++ b/testfiles/cli_tests/testcases/regression-1364.svg @@ -0,0 +1,20 @@ + + + + + + + diff --git a/testfiles/cli_tests/testcases/regression-1364_script.py b/testfiles/cli_tests/testcases/regression-1364_script.py new file mode 100644 index 0000000..0d3b12a --- /dev/null +++ b/testfiles/cli_tests/testcases/regression-1364_script.py @@ -0,0 +1,31 @@ +from lxml import etree + +def d_cmp(orig, new): + """ Compares the original d attribute to the new one. """ + orig_list = orig.split() + new_list = new.split() + + if len(orig_list) != len(new_list): + return False + + # Normalize the final 'z' to uppercase: + orig_list[-1] = orig_list[-1].upper() + new_list[-1] = new_list[-1].upper() + + for (o, n) in zip(orig_list, new_list): + if o == n: + continue + numeric = "{:.0f}".format(float(n)) + if o != numeric: + return False + + return True + +document = etree.parse("regression-1364_output.svg") +layer = document.find('{http://www.w3.org/2000/svg}g[@id="layer1"]') +boolop_result = layer.find('{http://www.w3.org/2000/svg}path[@id="small"]') + +assert boolop_result.attrib.get("transform") == "scale(2)" + +assert d_cmp("M 0 0 L 0 50 A 50 50 0 0 0 50 0 L 0 0 z", boolop_result.attrib.get("d")) + diff --git a/testfiles/cli_tests/testcases/regression-2602.svg b/testfiles/cli_tests/testcases/regression-2602.svg new file mode 100644 index 0000000..0611017 --- /dev/null +++ b/testfiles/cli_tests/testcases/regression-2602.svg @@ -0,0 +1,9 @@ + + + + R + B + + + diff --git a/testfiles/cli_tests/testcases/regression-2602_script.py b/testfiles/cli_tests/testcases/regression-2602_script.py new file mode 100644 index 0000000..1a5480f --- /dev/null +++ b/testfiles/cli_tests/testcases/regression-2602_script.py @@ -0,0 +1,10 @@ +from lxml import etree + +document = etree.parse("regression-2602_output.svg") + +parent = document.find('{http://www.w3.org/2000/svg}g[@id="parent"]') +paths = parent.findall('{http://www.w3.org/2000/svg}path') + +assert parent.attrib.get("style") == "fill:#0000ff" +assert paths[0].attrib.get("style") == "fill:#ff0000" +assert paths[1].attrib.get("style") is None diff --git a/testfiles/cli_tests/testcases/regression-2797.svg b/testfiles/cli_tests/testcases/regression-2797.svg new file mode 100644 index 0000000..cb62158 --- /dev/null +++ b/testfiles/cli_tests/testcases/regression-2797.svg @@ -0,0 +1,22 @@ + + + + + + + Outside tspan + Inside tspan + + diff --git a/testfiles/cli_tests/testcases/regression-2797_script.py b/testfiles/cli_tests/testcases/regression-2797_script.py new file mode 100644 index 0000000..639bff4 --- /dev/null +++ b/testfiles/cli_tests/testcases/regression-2797_script.py @@ -0,0 +1,17 @@ +from lxml import etree + +document = etree.parse("regression-2797_output.svg") + +parent = document.find('{http://www.w3.org/2000/svg}text[@id="parent"]') +tspan1, = parent.findall('{http://www.w3.org/2000/svg}tspan') +tspan2s = tspan1.findall('{http://www.w3.org/2000/svg}tspan') + +# Expect outer tspan added as SVG 1.1 fallback with x/y position. +# Expect no inner tspan with incorrect "font-size:medium". + +assert len(tspan2s) == 0 + +assert parent.attrib.get("style") == "font-size:4px;shape-inside:url(#theshape)" +assert tspan1.attrib.get("style") is None +assert tspan1.attrib.get("x") == "2" +assert tspan1.attrib.get("y") is not None diff --git a/testfiles/cli_tests/testcases/shapes.svg b/testfiles/cli_tests/testcases/shapes.svg new file mode 100644 index 0000000..61ad01d --- /dev/null +++ b/testfiles/cli_tests/testcases/shapes.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/testfiles/cli_tests/testcases/shapes_expected.emf b/testfiles/cli_tests/testcases/shapes_expected.emf new file mode 100644 index 0000000..4bf1faa Binary files /dev/null and b/testfiles/cli_tests/testcases/shapes_expected.emf differ diff --git a/testfiles/cli_tests/testcases/shapes_expected.eps b/testfiles/cli_tests/testcases/shapes_expected.eps new file mode 100644 index 0000000..7b5a31a --- /dev/null +++ b/testfiles/cli_tests/testcases/shapes_expected.eps @@ -0,0 +1,111 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: cairo 1.16.0 (https://cairographics.org) +%%CreationDate: Tue Mar 28 19:58:58 2023 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%BoundingBox: 6 3 298 223 +%%EndComments +%%BeginProlog +50 dict begin +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageBoundingBox: 6 3 298 223 +%%EndPageSetup +q 6 3 292 220 rectclip +1 0 0 -1 0 226 cm q +0 0 1 rg +7.5 3.75 120 97.5 re f +0 g +1.5 w +0 J +0 j +[] 0.0 d +4 M q 1 0 0 1 0 0 cm +7.5 3.75 120 97.5 re S Q +1 0 0 rg +296.25 97.5 m 296.25 138.922 255.957 172.5 206.25 172.5 c 156.543 172.5 + 116.25 138.922 116.25 97.5 c 116.25 56.078 156.543 22.5 206.25 22.5 c 255.957 + 22.5 296.25 56.078 296.25 97.5 c h +296.25 97.5 m f +0 g +3 w +q 1 0 0 1 0 0 cm +296.25 97.5 m 296.25 138.922 255.957 172.5 206.25 172.5 c 156.543 172.5 + 116.25 138.922 116.25 97.5 c 116.25 56.078 156.543 22.5 206.25 22.5 c 255.957 + 22.5 296.25 56.078 296.25 97.5 c h +296.25 97.5 m S Q +1 1 0 rg +71.25 217.5 m 78.715 151.992 l 22.863 116.957 l 87.469 103.812 l 103.535 + 39.867 l 136 97.25 l 201.777 92.766 l 157.234 141.375 l 181.824 202.551 + l 121.832 175.207 l h +71.25 217.5 m f +0 g +4.5 w +q 1 0 0 1 0 0 cm +71.25 217.5 m 78.715 151.992 l 22.863 116.957 l 87.469 103.812 l 103.535 + 39.867 l 136 97.25 l 201.777 92.766 l 157.234 141.375 l 181.824 202.551 + l 121.832 175.207 l h +71.25 217.5 m S Q +Q Q +showpage +%%Trailer +end +%%EOF diff --git a/testfiles/cli_tests/testcases/shapes_expected.pdf b/testfiles/cli_tests/testcases/shapes_expected.pdf new file mode 100644 index 0000000..bff07b6 Binary files /dev/null and b/testfiles/cli_tests/testcases/shapes_expected.pdf differ diff --git a/testfiles/cli_tests/testcases/shapes_expected.png b/testfiles/cli_tests/testcases/shapes_expected.png new file mode 100644 index 0000000..3e81da4 Binary files /dev/null and b/testfiles/cli_tests/testcases/shapes_expected.png differ diff --git a/testfiles/cli_tests/testcases/shapes_expected.ps b/testfiles/cli_tests/testcases/shapes_expected.ps new file mode 100644 index 0000000..9037026 --- /dev/null +++ b/testfiles/cli_tests/testcases/shapes_expected.ps @@ -0,0 +1,146 @@ +%!PS-Adobe-3.0 +%%Creator: cairo 1.16.0 (https://cairographics.org) +%%CreationDate: Sat Feb 22 20:44:25 2020 +%%Pages: 1 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%DocumentMedia: 106x79mm 300 225 0 () () +%%BoundingBox: 6 2 298 222 +%%EndComments +%%BeginProlog +/languagelevel where +{ pop languagelevel } { 1 } ifelse +2 lt { /Helvetica findfont 12 scalefont setfont 50 500 moveto + (This print job requires a PostScript Language Level 2 printer.) show + showpage quit } if +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +/cairo_data_source { + CairoDataIndex CairoData length lt + { CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def } + { () } ifelse +} def +/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def +/cairo_image { image cairo_flush_ascii85_file } def +/cairo_imagemask { imagemask cairo_flush_ascii85_file } def +/cairo_set_page_size { + % Change paper size, but only if different from previous paper size otherwise + % duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size + % so we use the same when checking if the size changes. + /setpagedevice where { + pop currentpagedevice + /PageSize known { + 2 copy + currentpagedevice /PageSize get aload pop + exch 4 1 roll + sub abs 5 gt + 3 1 roll + sub abs 5 gt + or + } { + true + } ifelse + { + 2 array astore + 2 dict begin + /PageSize exch def + /ImagingBBox null def + currentdict end + setpagedevice + } { + pop pop + } ifelse + } { + pop + } ifelse +} def +%%EndProlog +%%BeginSetup +%%EndSetup +%%Page: 1 1 +%%BeginPageSetup +%%PageMedia: 106x79mm +%%PageBoundingBox: 6 2 298 222 +300 225 cairo_set_page_size +%%EndPageSetup +q 6 2 292 220 rectclip +1 0 0 -1 0 225 cm q +0 0 1 rg +7.5 3.75 120 97.5 re f +0 g +1.5 w +0 J +0 j +[] 0.0 d +4 M q 1 0 0 1 0 0 cm +7.5 3.75 120 97.5 re S Q +1 0 0 rg +296.25 97.5 m 296.25 138.922 255.957 172.5 206.25 172.5 c 156.543 172.5 + 116.25 138.922 116.25 97.5 c 116.25 56.078 156.543 22.5 206.25 22.5 c 255.957 + 22.5 296.25 56.078 296.25 97.5 c f +0 g +3 w +q 1 0 0 1 0 0 cm +296.25 97.5 m 296.25 138.922 255.957 172.5 206.25 172.5 c 156.543 172.5 + 116.25 138.922 116.25 97.5 c 116.25 56.078 156.543 22.5 206.25 22.5 c 255.957 + 22.5 296.25 56.078 296.25 97.5 c S Q +1 1 0 rg +71.25 217.5 m 78.715 151.992 l 22.863 116.957 l 87.469 103.813 l 103.535 + 39.867 l 136 97.25 l 201.777 92.766 l 157.234 141.375 l 181.824 202.551 + l 121.832 175.207 l h +71.25 217.5 m f +0 g +4.5 w +q 1 0 0 1 0 0 cm +71.25 217.5 m 78.715 151.992 l 22.863 116.957 l 87.469 103.813 l 103.535 + 39.867 l 136 97.25 l 201.777 92.766 l 157.234 141.375 l 181.824 202.551 + l 121.832 175.207 l h +71.25 217.5 m S Q +Q Q +showpage +%%Trailer +%%EOF diff --git a/testfiles/cli_tests/testcases/shapes_expected.wmf b/testfiles/cli_tests/testcases/shapes_expected.wmf new file mode 100644 index 0000000..1b3fdcf Binary files /dev/null and b/testfiles/cli_tests/testcases/shapes_expected.wmf differ diff --git a/testfiles/cli_tests/testcases/shapes_expected.xaml b/testfiles/cli_tests/testcases/shapes_expected.xaml new file mode 100644 index 0000000..9c9e55a --- /dev/null +++ b/testfiles/cli_tests/testcases/shapes_expected.xaml @@ -0,0 +1,2 @@ + + diff --git a/testfiles/cli_tests/testcases/shapes_pre_0.92.svg b/testfiles/cli_tests/testcases/shapes_pre_0.92.svg new file mode 100644 index 0000000..058fabf --- /dev/null +++ b/testfiles/cli_tests/testcases/shapes_pre_0.92.svg @@ -0,0 +1,9 @@ + + + + + + + + diff --git a/testfiles/cli_tests/testcases/shapes_pre_0.92_scaled_expected.png b/testfiles/cli_tests/testcases/shapes_pre_0.92_scaled_expected.png new file mode 100644 index 0000000..4ca40fb Binary files /dev/null and b/testfiles/cli_tests/testcases/shapes_pre_0.92_scaled_expected.png differ diff --git a/testfiles/cli_tests/testcases/square_mm.svg b/testfiles/cli_tests/testcases/square_mm.svg new file mode 100644 index 0000000..b52aec6 --- /dev/null +++ b/testfiles/cli_tests/testcases/square_mm.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/testfiles/cli_tests/testcases/square_mm_viewbox.svg b/testfiles/cli_tests/testcases/square_mm_viewbox.svg new file mode 100644 index 0000000..394b205 --- /dev/null +++ b/testfiles/cli_tests/testcases/square_mm_viewbox.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/testfiles/cli_tests/testcases/square_px.svg b/testfiles/cli_tests/testcases/square_px.svg new file mode 100644 index 0000000..7467f7d --- /dev/null +++ b/testfiles/cli_tests/testcases/square_px.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/testfiles/cli_tests/testcases/stroke-to-path-variations.svg b/testfiles/cli_tests/testcases/stroke-to-path-variations.svg new file mode 100644 index 0000000..2c8e04b --- /dev/null +++ b/testfiles/cli_tests/testcases/stroke-to-path-variations.svg @@ -0,0 +1,131 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SVG 1.1 + inline size + + SVG shape-inside + SVG1.2 flowRoot + + + + + + + + + + diff --git a/testfiles/cli_tests/testcases/systemLanguage.svg b/testfiles/cli_tests/testcases/systemLanguage.svg new file mode 100644 index 0000000..c650dc9 --- /dev/null +++ b/testfiles/cli_tests/testcases/systemLanguage.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/testfiles/cli_tests/testcases/systemLanguage_RDF.svg b/testfiles/cli_tests/testcases/systemLanguage_RDF.svg new file mode 100644 index 0000000..7e1223e --- /dev/null +++ b/testfiles/cli_tests/testcases/systemLanguage_RDF.svg @@ -0,0 +1,15 @@ + + + + + + fr + + + + + diff --git a/testfiles/cli_tests/testcases/systemLanguage_de.png b/testfiles/cli_tests/testcases/systemLanguage_de.png new file mode 100644 index 0000000..1dbfe36 Binary files /dev/null and b/testfiles/cli_tests/testcases/systemLanguage_de.png differ diff --git a/testfiles/cli_tests/testcases/systemLanguage_default.png b/testfiles/cli_tests/testcases/systemLanguage_default.png new file mode 100644 index 0000000..67cb9f0 Binary files /dev/null and b/testfiles/cli_tests/testcases/systemLanguage_default.png differ diff --git a/testfiles/cli_tests/testcases/systemLanguage_en.png b/testfiles/cli_tests/testcases/systemLanguage_en.png new file mode 100644 index 0000000..31679aa Binary files /dev/null and b/testfiles/cli_tests/testcases/systemLanguage_en.png differ diff --git a/testfiles/cli_tests/testcases/systemLanguage_fr.png b/testfiles/cli_tests/testcases/systemLanguage_fr.png new file mode 100644 index 0000000..deeb4bf Binary files /dev/null and b/testfiles/cli_tests/testcases/systemLanguage_fr.png differ diff --git a/testfiles/cli_tests/testcases/systemLanguage_pt.png b/testfiles/cli_tests/testcases/systemLanguage_pt.png new file mode 100644 index 0000000..91e5b4b Binary files /dev/null and b/testfiles/cli_tests/testcases/systemLanguage_pt.png differ diff --git a/testfiles/cli_tests/testcases/text.svg b/testfiles/cli_tests/testcases/text.svg new file mode 100644 index 0000000..5dd1c7f --- /dev/null +++ b/testfiles/cli_tests/testcases/text.svg @@ -0,0 +1,4 @@ + + + some text + diff --git a/testfiles/cli_tests/testcases/text_in_group.svg b/testfiles/cli_tests/testcases/text_in_group.svg new file mode 100644 index 0000000..47719d0 --- /dev/null +++ b/testfiles/cli_tests/testcases/text_in_group.svg @@ -0,0 +1,11 @@ + + + + some text + + + + some text + + + diff --git a/testfiles/cli_tests/testcases/theta.svg b/testfiles/cli_tests/testcases/theta.svg new file mode 100644 index 0000000..00cb1f7 --- /dev/null +++ b/testfiles/cli_tests/testcases/theta.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/testfiles/data/crystalegg.xml b/testfiles/data/crystalegg.xml new file mode 100644 index 0000000..0e916e7 --- /dev/null +++ b/testfiles/data/crystalegg.xml @@ -0,0 +1,767 @@ + + + + + + The Crystal Egg + H. G.Wells + + + + + + + There was, until a year ago, a little and very grimy-looking shop +near Seven Dials over which, in weather-worn yellow lettering, the name +of "C. Cave, Naturalist and Dealer in Antiquities," was inscribed. The +contents of its window were curiously variegated. They comprised some +elephant tusks and an imperfect set of chessmen, beads and weapons, a +box of eyes, two skulls of tigers and one human, several moth-eaten +stuffed monkeys (one holding a lamp), an old-fashioned cabinet, a +flyblown ostrich egg or so, some fishing-tackle, and an extraordinarily +dirty, empty glass fish tank. There was also, at the moment the story +begins, a mass of crystal, worked into the shape of an egg and +brilliantly polished. And at that two people, who stood outside the +window, were looking, one of them a tall, thin clergyman, the other a +black-bearded young man of dusky complexion and unobtrusive costume. The +dusky young man spoke with eager gestulation, and seemed anxious for his +companion to purchase the article. + + + + While they were there, Mr. Cave came into his shop, his beard still +wagging with the bread and butter of his tea. When he saw these men and +the object of their regard, his countenance fell. He glanced guiltily +over his shoulder, and softly shut the door. He was a little old man, +with pale face and peculiar watery blue eyes; his hair was a dirty grey, +and he wore a shabby blue frock-coat, an ancient silk hat, and carpet +slippers very much down at heel. He remained watching the two men as +they talked. The clergyman went deep into his trouser pocket, examined a +handful of money, and showed his teeth in an agreeable smile. Mr. Cave +seemed still more depressed when they came into the shop. + + + + The clergyman, without any ceremony, asked the price of the crystal +egg. Mr. Cave glanced nervously towards the door leading into the +parlour, and said five pounds. The clergyman protested that the price +was high, to his companion as well as to Mr. Cave -- it was, indeed, +very much more than Mr. Cave had intended to ask, when he had stocked +the article -- and an attempt at bargaining ensued. Mr. Cave stepped to +the shop-door, and held it open. "Five pounds is my price," he said, as +though he wished to save himself the trouble of unprofitable discussion. +As he did so, the upper portion of a woman's face appeared above the +blind in the glass upper panel of the door leading into the parlour, and +stared curiously at the two customers. "Five pounds is my price," said +Mr. Cave, with a quiver in his voice. + + + + The swarthy young man had so far remained a spectator, watching Cave +keenly. Now he spoke. "Give him five pounds," he said. The clergyman +glanced at him to see if he were in earnest, and, when he looked at Mr. +Cave again, he saw that the latter's face was white. "It's a lot of +money," said the clergyman, and, diving into his pocket, began counting +his resources. He had little more than thirty shillings, and he appealed +to his companion, with whom he seemed to be on terms of considerable +intimacy. This gave Mr. Cave an opportunity of collecting his thoughts, +and he began to explain in an agitated manner that the crystal was not, +as a matter of fact, entirely free for sale. His two customers were +naturally surprised at this, and inquired why he had not thought of that +before he began to bargain. Mr. Cave became confused, but he stuck to +his story, that the crystal was not in the market that afternoon, that a +probable purchaser of it had already appeared. The two, treating this as +an attempt to raise the price still further, made as if they would leave +the shop. But at this point the parlour door opened, and the owner of +the dark fringe and the little eyes appeared. + + + + She was a coarse-featured, corpulent woman, younger and very much +larger than Mr. Cave; she walked heavily, and her face was flushed. +"That crystal is for sale, she said. "And five pounds is a good enough +price for it. I can't think what you're about, Cave, not to take the +gentleman's offer!" + + + + Mr. Cave, greatly perturbed by the irruption, looked angrily at her +over the rims of his spectacles, and, without excessive assurance, +asserted his right to manage his business in his own way. An altercation +began. The two customers watched the scene with interest and some +amusement, occasionally assisting Mrs. Cave with suggestions. Mr. Cave, +hard driven, persisted in a confused and impossible story of an enquiry +for the crystal that morning, and his agitation became painful. But he +stuck to his point with extraordinary persistence. +It was the young Oriental who ended this curious controversy. He +proposed that they should call again in the course of two days -- so as +to give the alleged enquirer a fair chance. "And then we must insist," +said the clergyman. "Five pounds." Mrs. Cave took it on herself to +apologise for her husband, explaining that he was sometimes "a little +odd," and as the two customers left, the couple prepared for a free +discussion of the incident in all its bearings. + + + + Mrs. Cave talked to her husband with singular directness. The poor +little man, quivering with emotion, muddled himself between his stories, +maintaining on the one hand that he had another customer in view, and on +the other asserting that the crystal was honestly worth ten guineas. +"Why did you ask five pounds?" said his wife. "Do let me manage my +business my own way!" said Mr. Cave. + + + + Mr. Cave had living with him a step-daughter and a step-son, and at +supper that night the transaction was re-discussed. None of them had a +high opinion of Mr. Cave's business methods, and this action seemed a +culminating folly. + + + + "It's my opinion he's refused that crystal before," said the +step-son, a loose-limbed lout of eighteen. + + + + "But Five Pounds!" said the step-daughter, an argumentative young +woman of six-and-twenty. + + + + Mr. Cave's answers were wretched; he could only mumble weak +assertions that he knew his own business best. They drove him from his +half-eaten supper into the shop, to close it for the night, his ears +aflame and tears of vexation behind his spectacles. "Why had he left the +crystal in the window so long? The folly of it!" That was the trouble +closest in his mind. For a time he could see no way of evading sale. + + + + After supper his step-daughter and step-son smartened themselves up +and went out and his wife retired upstairs to reflect upon the business +aspects of the crystal, over a little sugar and lemon and so forth in +hot water. Mr. Cave went into the shop, and stayed there until late, +ostensibly to make ornamental rockeries for gold-fish cases but really +for a private purpose that will be better explained later. The next day +Mrs. Cave found that the crystal had been removed from the window, and +was lying behind some second-hand books on angling. She replaced it in a +conspicuous position. But she did not argue further about it, as a +nervous headache disinclined her from debate. Mr. Cave was always +disinclined. The day passed disagreeably. Mr. Cave was, if anything, +more absent-minded than usual, and uncommonly irritable withal. In the +afternoon, when his wife was taking her customary sleep, he removed the +crystal from the window again. + + + + The next day Mr. Cave had to deliver a consignment of dog-fish at one +of the hospital schools, where they were needed for dissection. In his +absence Mrs. Cave's mind reverted to the topic of the crystal, and the +methods of expenditure suitable to a windfall of five pounds. She had +already devised some very agreeable expedients, among others a dress of +green silk for herself and a trip to Richmond, when a jangling of the +front door bell summoned her into the shop. The customer was an +examination coach who came to complain of the non-delivery of certain +frogs asked for the previous day. Mrs. Cave did not approve of this +particular branch of Mr. Cave's business, and the gentleman, who had +called in a somewhat aggressive mood, retired after a brief exchange of +words -- entirely civil so far as he was concerned. Mrs. Cave's eye then +naturally turned to the window; for the sight of the crystal was an +assurance of the five pounds and of her dreams. What was her surprise to +find it gone! + + + + She went to the place behind the locker on the counter, where she had +discovered it the day before. It was not there; and she immediately +began an eager search about the shop. + + + + When Mr. Cave returned from his business with the dog-fish, about a +quarter to two in the afternoon, he found the shop in some confusion, +and his wife, extremely exasperated and on her knees behind the counter, +routing among his taxidermic material. Her face came up hot and angry +over the counter, as the jangling bell announced his return, and she +forthwith accused him of "hiding it." + + + + "Hid what?" asked Mr. Cave. + + + + "The crystal!" + + + + At that Mr. Cave, apparently much surprised, rushed to the window. +"Isn't it here?" he said. "Great Heavens! what has become of it?" + + + + Just then, Mr. Cave's step-son re-entered the shop from the inner +room -- he had come home a minute or so before Mr. Cave -- and he was +blaspheming freely. He was apprenticed to a second-hand furniture dealer +down the road, but he had his meals at home, and he was naturally +annoyed to find no dinner ready. + + + + But, when he heard of the loss of the crystal, he forgot his meal, +and his anger was diverted from his mother to his step-father. Their +first idea, of course, was that he had hidden it. But Mr. Cave stoutly +denied all knowledge of its fate -- freely offering his bedabbled +affidavit in the matter -- and at last was worked up to the point of +accusing, first, his wife and then his step-son of having taken it with +a view to a private sale. So began an exceedingly acrimonious and +emotional discussion, which ended for Mrs. Cave in a peculiar nervous +condition midway between hysterics and amuck, and caused the step-son to +be half-an-hour late at the furniture establishment in the afternoon. +Mr. Cave took refuge from his wife's emotions in the shop. + + + + In the evening the matter was resumed, with less passion and in a +judicial spirit, under the presidency of the step-daughter. The supper +passed unhappily and culminated in a painful scene. Mr. Cave gave way at +last to extreme exasperation, and went out banging the front door +violently. The rest of the family, having discussed him with the freedom +his absence warranted, hunted the house from garret to cellar, hoping to +light upon the crystal. + + + + The next day the two customers called again. They were received by +Mrs. Cave almost in tears. It transpired that no one could imagine all +that she had stood from Cave at various times in her married pilgrimage. +. . . She also gave a garbled account of the disappearance. The +clergyman and the Oriental laughed silently at one another, and said it +was very extraordinary. As Mrs. Cave seemed disposed to give them the +complete history of her life they made to leave the shop. Thereupon Mrs. +Cave, still clinging to hope, asked for the clergyman's address, so +that, if she could get anything out of Cave, she might communicate it. +The address was duly given, but apparently was afterwards mislaid. Mrs. +Cave can remember nothing about it. + + + + In the evening of that day, the Caves seem to have exhausted their +emotions, and Mr. Cave, who had been out in the afternoon, supped in a +gloomy isolation that contrasted pleasantly with the impassioned +controversy of the previous days. For some time matters were very badly +strained in the Cave household, but neither crystal nor customer +reappeared. + + + + Now, without mincing the matter, we must admit that Mr. Cave was a +liar. He knew perfectly well where the crystal was. It was in the rooms +of Mr. Jacoby Wace, Assistant Demonstrator at St. Catherine's Hospital, +Westbourne Street. It stood on the sideboard partially covered by a +black velvet cloth, and beside a decanter of American whisky. It is from +Mr. Wace, indeed, that the particulars upon which this narrative is +based were derived. Cave had taken off the thing to the hospital hidden +in the dog-fish sack, and there had pressed the young investigator to +keep it for him. Mr. Wace was a little dubious at first. His +relationship to Cave was peculiar. He had a taste for singular +characters, and he had more than once invited the old man to smoke and +drink in his rooms, and to unfold his rather amusing views of life in +general and of his wife in particular. Mr. Wace had encountered Mrs. +Cave, too, on occasions when Mr. Cave was not at home to attend to him. +He knew the constant interference to which Cave was subjected, and +having weighed the story judicially, he decided to give the crystal a +refuge. Mr. Cave promised to explain the reasons for his remarkable +affection for the crystal more fully +on a later occasion, but he spoke distinctly of seeing visions therein. +He called on Mr. Wace the same evening. + + + + He told a complicated story. The crystal he said had come into his +possession with other oddments at the forced sale of another curiosity +dealer's effects, and not knowing what its value might be, he had +ticketed it at ten shillings. It had hung upon his hands at that price +for some months, and he was thinking of "reducing the figure," when he +made a singular discovery. + + + + At that time his health was very bad -- and it must be borne in mind +that, throughout all this experience, his physical condition was one of +ebb -- and he was in considerable distress by reason of the negligence, +the positive ill-treatment even, he received from his wife and +step-children. His wife was vain, extravagant, unfeeling and had a +growing taste for private drinking; his step-daughter was mean and +over-reaching; and his step-son had conceived a violent dislike for him, +and lost no chance of showing it. The requirements of his business +pressed heavily upon him, and Mr. Wace does not think that he was +altogether free from occasional intemperance. He had begun life in a +comfortable position, he was a man of fair education, and he suffered, +for weeks at a stretch, from melancholia and insomnia. Afraid to disturb +his family, he would slip quietly from his wife's side, when his +thoughts became intolerable, and wander about the house. And about three +o'clock one morning, late in August, chance directed him into the shop. + + + + The dirty little place was impenetrably black except in one spot, +where he perceived an unusual glow of light. Approaching this, he +discovered it to be the crystal egg, which was standing on the corner of +the counter towards the window. A thin ray smote through a crack in the +shutters, impinged upon the object, and seemed as it were to fill its +entire interior. + + + + It occurred to Mr. Cave that this was not in accordance with the laws +of optics as he had known them in his younger days. He could understand +the rays being refracted by the crystal and coming to a focus in its +interior, but this diffusion jarred with his physical conceptions. He +approached the crystal nearly, peering into it and round it, with a +transient revival of the scientific curiosity that in his youth had +determined his choice of a calling. He was surprised to find the light +not steady, but writhing within the substance of the egg, as though that +object was a hollow sphere of some luminous vapour. In moving about to +get different points of view, he suddenly found that he had come between +it and the ray, and that the crystal none the less remained luminous. +Greatly astonished, he lifted it out of the light ray and carried it to +the darkest part of the shop. It remained +bright for some four or five minutes, when it slowly faded and went out. +He placed it in the thin streak of daylight, and its luminousness was +almost immediately restored. + + + + So far, at least, Mr. Wace was able to verify the remarkable story of +Mr. Cave. He has himself repeatedly held this crystal in a ray of light +(which had to be of a less diameter than one millimetre). And in a +perfect darkness, such as could be produced by velvet wrapping, the +crystal did undoubtedly appear very faintly phosphorescent. It would +seem, however, that the luminousness was of some exceptional sort, and +not equally visible to all eyes; for Mr. Harbinger -- whose name will be +familiar to the scientific reader in connection with the Pasteur +Institute -- was quite unable to see any light whatever. And Mr. Wace's +own capacity for its appreciation was out of comparison inferior to that +of Mr. Cave's. Even with Mr. Cave the power varied very considerably: +his vision was most vivid during states of extreme weakness and fatigue. + + + + Now, from the outset this light in the crystal exercised a curious +fascination upon Mr. Cave. And it says more for his loneliness of soul +than a volume of pathetic writing could do, that he told no human being +of his curious observations. He seems to have been living in such an +atmosphere of petty spite that to admit the existence of a pleasure +would have been to risk the loss of it. He found that as the dawn +advanced, and the amount of diffused light increased, the crystal became +to all appearance non-luminous. And for some time he was unable to see +anything in it, except at night-time, in dark corners of the shop. + + + + But the use of an old velvet cloth, which he used as a background for +a collection of minerals, occurred to him, and by doubling this, and +putting it over his head and hands, he was able to get a sight of the +luminous movement within the crystal even in the day-time. He was very +cautious lest he should be thus discovered by his wife, and he practised +this occupation only in the afternoons, while she was asleep upstairs, +and then circumspectly in a hollow under the counter. And one day, +turning the crystal about in his hands, he saw something. It came and +went like a flash, but it gave him the impression that the object had +for a moment opened to him the view of a wide and spacious and strange +country; and, turning it about, he did, just as the light faded, see the +same vision again. + + + + Now, it would be tedious and unnecessary to state all the phases of +Mr. Cave's discovery from this point. Suffice that the effect was this: +the crystal, being peered into at an angle of about 137 degrees from the +direction of the illuminating ray, gave a clear and consistent picture +of a wide and peculiar country-side. It was not dream-like at +all: it produced a definite impression of reality, and the better the +light the more real and solid it seemed. It was a moving picture: that +is to say, certain objects moved in it, but slowly in an orderly manner +like real things, and, according as the direction of the lighting and +vision changed, the picture changed also. It must, indeed, have been +like looking through an oval glass at a view, and turning the glass +about to get at different aspects. + + + + Mr. Cave's statements, Mr. Wace assures me, were extremely +circumstantial, and entirely free from any of that emotional quality +that taints hallucinatory impressions. But it must be remembered that +all the efforts of Mr. Wace to see any similar clarity in the faint +opalescence of the crystal were wholly unsuccessful, try as he would. +The difference in intensity of the impressions received by the two men +was very great, and it is quite conceivable that what was a view to Mr. +Cave was a mere blurred nebulosity to Mr. Wace. + + + + The view, as Mr. Cave described it, was invariably of an extensive +plain, and he seemed always to be looking at it from a considerable +height, as if from a tower or a mast. To the east and to the west the +plain was bounded at a remote distance by vast reddish cliffs, which +reminded him of those he had seen in some picture; but what the picture +was Mr. Wace was unable to ascertain. These cliffs passed north and +south -- he could tell the points of the compass by the stars that were +visible of a night -- receding in an almost illimitable perspective and +fading into the mists of the distance before they met. He was nearer the +eastern set of cliffs, on the occasion of his first vision the sun was +rising over them, and black against the sunlight and pale against their +shadow appeared a multitude of soaring forms that Mr. Cave regarded as +birds. A vast range of buildings spread below him; he seemed to be +looking down upon them; and, as they approached the blurred and +refracted edge of the picture, they became indistinct. There were also +trees curious in shape, and in colouring, a deep mossy green and an +exquisite grey, beside a wide and shining canal. And something great and +brilliantly coloured flew across the picture. But the first time Mr. +Cave saw these pictures he saw only in flashes, his hands shook, his +head moved, the vision came and went, and grew foggy and indistinct. And +at first he had the greatest difficulty in finding the picture again +once the direction of it was lost. + + + + His next clear vision, which came about a week after the first, the +interval having yielded nothing but tantalising glimpses and some useful +experience, showed him the view down the length of the valley. The view +was different, but he had a curious persuasion, which his subsequent +observations abundantly confirmed, that he was regarding this strange +world from exactly the same spot, although he was looking +in a different direction. The long facade of the great building, whose +roof he had looked down upon before, was now receding in perspective. He +recognised the roof. In the front of the facade was a terrace of massive +proportions and extraordinary length, and down the middle of the +terrace, at certain intervals, stood huge but very graceful masts, +bearing small shiny objects which reflected the setting sun. The import +of these small objects did not occur to Mr. Cave until some time after, +as he was describing the scene to Mr. Wace. The terrace overhung a +thicket of the most luxuriant and graceful vegetation, and beyond this +was a wide grassy lawn on which certain broad creatures, in form like +beetles but enormously larger, reposed. Beyond this again was a richly +decorated causeway of pinkish stone; and beyond that, and lined with +dense red weeds, and passing up the valley exactly parallel with the +distant cliffs, was a broad and mirror-like expanse of water. The air +seemed full of squadrons of great birds, manoeuvring in stately curves; +and across the river was a multitude of splendid buildings, richly +coloured and glittering with metallic tracery and facets, among a forest +of moss-like and lichenous trees. And suddenly something flapped +repeatedly across the vision, like the fluttering of a jewelled fan or +the beating of a wing, and a face, or rather the upper part of a face +with very large eyes, came as it were close to his own and as if on the +other side of the crystal. Mr. Cave was so startled and so impressed by +the absolute reality of these eyes, that he drew his head back from the +crystal to look behind it. He had become so absorbed in watching that he +was quite surprised to find himself in the cool darkness of his little +shop, with its familiar odour of methyl, mustiness, and decay. And, as +he blinked about him, the glowing crystal faded, and went out. + + + + Such were the first general impressions of Mr. Cave. The story is +curiously direct and circumstantial. From the outset, when the valley +first flashed momentarily on his senses, his imagination was strangely +affected, and, as he began to appreciate the details of the scene he +saw, his wonder rose to the point of a passion. He went about his +business listless and distraught, thinking only of the time when he +should be able to return to his watching. And then a few weeks after his +first sight of the valley came the two customers, the stress and +excitement of their offer, and the narrow escape of the crystal from +sale, as I have already told. + + + + Now, while the thing was Mr. Cave's secret, it remained a mere +wonder, a thing to creep to covertly and peep at, as a child might peep +upon a forbidden garden. But Mr. Wace has, for a young scientific +investigator, a particularly lucid and consecutive habit of mind. +Directly the crystal and its story came to him, and he had satisfied +himself, by seeing the phosphorescence with his own eyes, that there +really was a certain evidence for Mr. Cave's statements, he proceeded to +develop the matter systematically. Mr. Cave was only too eager to come +and feast his eyes on this wonderland he saw, and he came every night +from half-past eight until half-past ten, and sometimes, in Mr. Wace's +absence, during the day. On Sunday afternoons, also, he came. From the +outset Mr. Wace made copious notes, and it was due to his scientific +method that the relation between the direction from which the initiating +ray entered the crystal and the orientation of the picture were proved. +And, by covering the crystal in a box perforated only with a small +aperture to admit the exciting ray, and by substituting black holland +for his buff blinds, he greatly improved the conditions of the +observations; so that in a little while they were able to survey the +valley in any direction they desired. + + + + So having cleared the way, we may give a brief account of this +visionary world within the crystal. The things were in all cases seen by +Mr. Cave, and the method of working was invariably for him to watch the +crystal and report what he saw, while Mr. Wace (who as a science student +had learnt the trick of writing in the dark) wrote a brief note of his +report. When the crystal faded, it was put into its box in the proper +position and the electric light turned on. Mr. Wace asked questions, and +suggested observations to clear up difficult points. Nothing, indeed, +could have been less visionary and more matter-of-fact. + + + + The attention of Mr. Cave had been speedily directed to the bird-like +creatures he had seen so abundantly present in each of his earlier +visions. His first impression was soon corrected, and he considered for +a time that they might represent a diurnal species of bat. Then he +thought, grotesquely enough, that they might be cherubs. Their heads +were round, and curiously human, and it was the eyes of one of them that +had so startled him on his second observation. They had broad, silvery +wings, not feathered, but glistening almost as brilliantly as new-killed +fish and with the same subtle play of colour, and these wings were not +built on the plan of a bird-wing or bat, Mr. Wace learned, but supported +by curved ribs radiating from the body. (A sort of butterfly wing with +curved ribs seems best to express their appearance.) The body was small, +but fitted with two bunches of prehensile organs, like long tentacles, +immediately under the mouth. Incredible as it appeared to Mr. Wace, the +persuasion at last became irresistible, that it was these creatures +which owned the great quasi-human buildings and the magnificent garden +that made the broad valley so splendid. And Mr. Cave perceived that the +buildings, with other peculiarities, had no doors, but that the great +circular windows, which +opened freely, gave the creatures egress and entrance. They would alight +upon their tentacles, fold their wings to a smallness almost rod-like, +and hop into the interior. But among them was a multitude of +smaller-winged creatures, like great dragon-flies and moths and flying +beetles, and across the greensward brilliantly-coloured gigantic +ground-beetles crawled lazily to and fro. Moreover, on the causeways and +terraces, large-headed creatures similar to the greater winged flies, +but wingless, were visible, hopping busily upon their hand-like tangle +of tentacles. + + + + Allusion has already been made to the glittering objects upon masts +that stood upon the terrace of the nearer building. It dawned upon Mr. +Cave, after regarding one of these masts very fixedly on one +particularly vivid day, that the glittering object there was a crystal +exactly like that into which he peered. And a still more careful +scrutiny convinced him that each one in a vista of nearly twenty carried +a similar object. + + + + Occasionally one of the large flying creatures would flutter up to +one, and, folding its wings and coiling a number of its tentacles about +the mast, would regard the crystal fixedly for a space, -- sometimes for +as long as fifteen minutes. And a series of observations, made at the +suggestion of Mr. Wace, convinced both watchers that, so far as this +visionary world was concerned, the crystal into which they peered +actually stood at the summit of the end-most mast on the terrace, and +that on one occasion at least one of these inhabitants of this other +world had looked into Mr. Cave's face while he was making these +observations. + + + + So much for the essential facts of this very singular story. Unless +we dismiss it all as the ingenious fabrication of Mr. Wace, we have to +believe one of two things: either that Mr. Cave's crystal was in two +worlds at once, and that, while it was carried about in one, it remained +stationary in the other, which seems altogether absurd; or else that it +had some peculiar relation of sympathy with another and exactly similar +crystal in this other world, so that what was seen in the interior of +the one in this world, was, under suitable conditions, visible to an +observer in the corresponding crystal in the other world; and vice +versa. At present, indeed, we do not know of any way in which two +crystals could so come en rapport, but nowadays we know enough to +understand that the thing is not altogether impossible. This view of the +crystals as en rapport was the supposition that occurred to Mr. Wace, +and to me at least it seems extremely plausible. . . . + + + + And where was this other world? On this, also, the alert intelligence +of Mr. Wace speedily threw light. After sunset, the sky darkened +rapidly -- there was a very brief twilight interval indeed -- and the +stars shone out. They were recognisably the same as those we see, +arranged in the same constellations. Mr. Cave recognised the Bear, the +Pleiades, Aldebaran, and Sirius: so that the other world must be +somewhere in the solar system, and, at the utmost, only a few hundreds +of millions of miles from our own. Following up this clue, Mr. Wace +learned that the midnight sky was a darker blue even than our midwinter +sky, and that the sun seemed a little smaller. And there were two small +moons! "like our moon but smaller, and quite differently marked" one of +which moved so rapidly that its motion was clearly visible as one +regarded it. These moons were never high in the sky, but vanished as +they rose: that is, every time they revolved they were eclipsed because +they were so near their primary planet. And all this answers quite +completely, although. Mr. Cave did not know it, to what must be the +condition of things on Mars. + + + + Indeed, it seems an exceedingly plausible conclusion that peering +into this crystal Mr. Cave did actually see the planet Mars and its +inhabitants. And, if that be the case, then the evening star that shone +so brilliantly in the sky of that distant vision, was neither more nor +less than our own familiar earth. + + + + For a time the Martians -- if they were Martians -- do not seem to +have known of Mr. Cave's inspection. Once or twice one would come to +peer, and go away very shortly to some other mast, as though the vision +was unsatisfactory. During this time Mr. Cave was able to watch the +proceedings of these winged people without being disturbed by their +attentions, and, although his report is necessarily vague and +fragmentary, it is nevertheless very suggestive. Imagine the impression +of humanity a Martian observer would get who, after a difficult process +of preparation and with considerable fatigue to the eyes, was able to +peer at London from the steeple of St. Martin's Church for stretches, at +longest, of four minutes at a time. Mr. Cave was unable to ascertain if +the winged Martians were the same as the Martians who hopped about the +causeways and terraces, and if the latter could put on wings at will. He +several times saw certain clumsy bipeds, dimly suggestive of apes, white +and partially translucent, feeding among certain of the lichenous trees, +and once some of these fled before one of the hopping, round-headed +Martians. The latter caught one in its tentacles, and then the picture +faded suddenly and left Mr. Cave most tantalisingly in the dark. On +another occasion a vast thing, that Mr. Cave thought at first was some +gigantic insect, appeared advancing along the causeway beside the canal +with extraordinary rapidity. As this drew nearer Mr. Cave perceived that +it was a mechanism of shining +metals and of extraordinary complexity. And then, when he looked again, +it had passed out of sight. + + + + After a time Mr. Wace aspired to attract the attention of the +Martians, and the next time that the strange eyes of one of them +appeared close to the crystal Mr. Cave cried out and sprang away, and +they immediately turned on the light and began to gesticulate in a +manner suggestive of signalling. But when at last Mr. Cave examined the +crystal again the Martian had departed. + + + + Thus far these observations had progressed in early November, and +then Mr. Cave, feeling that the suspicions of his family about the +crystal were allayed, began to take it to and fro with him in order +that, as occasion arose in the daytime or night, he might comfort +himself with what was fast becoming the most real thing in his existence. + + + + In December Mr. Wace's work in connection with a forthcoming +examination became heavy, the sittings were reluctantly suspended for a +week, and for ten or eleven days -- he is not quite sure which -- he saw +nothing of Cave. He then grew anxious to resume these investigations, +and, the stress of his seasonal labours being abated, he went down to +Seven Dials. At the corner he noticed a shutter before a bird fancier's +window, and then another at a cobbler's. Mr. Cave's shop was closed. + + + + He rapped and the door was opened by the step-son in black. He at +once called Mrs. Cave, who was, Mr. Wace could not but observe, in cheap +but ample widow's weeds of the most imposing pattern. Without any very +great surprise Mr. Wace learnt that Cave was dead and already buried. +She was in tears, and her voice was a little thick. She had just +returned from Highgate. Her mind seemed occupied with her own prospects +and the honourable details of the obsequies, but Mr. Wace was at last +able to learn the particulars of Cave's death. He had been found dead in +his shop in the early morning, the day after his last visit to Mr. Wace, +and the crystal had been clasped in his stone-cold hands. His face was +smiling, said Mrs. Cave, and the velvet cloth from the minerals lay on +the floor at his feet. He must have been dead five or six hours when he +was found. + + + + This came as a great shock to Wace, and he began to reproach himself +bitterly for having neglected the plain symptoms of the old man's +ill-health. But his chief thought was of the crystal. He approached that +topic in a gingerly manner, because he knew Mrs. Cave's peculiarities. +He was dumbfoundered to learn that it was sold. + + + + Mrs. Cave's first impulse, directly Cave's body had been taken +upstairs, had been to write to the mad clergyman who had offered five +pounds for the crystal, informing him of its recovery; but after a +violent hunt in which her daughter joined her, they were convinced +of the loss of his address. As they were without the means required to +mourn and bury Cave in the elaborate style the dignity of an old Seven +Dials inhabitant demands, they had appealed to a friendly +fellow-tradesman in Great Portland Street. He had very kindly taken over +a portion of the stock at a valuation. The valuation was his own and the +crystal egg was included in one of the lots. Mr. Wace, after a few +suitable consolatory observations, a little offhandedly proffered +perhaps, hurried at once to Great Portland Street. But there he learned +that the crystal egg had already been sold to a tall, dark man in grey. +And there the material facts in this curious, and to me at least very +suggestive, story come abruptly to an end. The Great Portland Street +dealer did not know who the tall dark man in grey was, nor had he +observed him with sufficient attention to describe him minutely. He did +not even know which way this person had gone after leaving the shop. For +a time Mr. Wace remained in the shop, trying the dealer's patience with +hopeless questions, venting his own exasperation. And at last, realising +abruptly that the whole thing had passed out of his hands, had vanished +like a vision of the night, he returned to his own rooms, a little +astonished to find the notes he had made still tangible and visible upon +his untidy table. + + + + His annoyance and disappointment were naturally very great. He made a +second call (equally ineffectual) upon the Great Portland Street dealer, +and he resorted to advertisements in such periodicals as were likely to +come into the hands of a bric-a-brac collector. He also wrote letters to +The Daily Chronicle and Nature, but both those periodicals, suspecting a +hoax, asked him to reconsider his action before they printed, and he was +advised that such a strange story, unfortunately so bare of supporting +evidence, might imperil his reputation as an investigator. Moreover, the +calls of his proper work were urgent. So that after a month or so, save +for an occasional reminder to certain dealers, he had reluctantly to +abandon the quest for the crystal egg, and from that day to this it +remains undiscovered. Occasionally, however, he tells me, and I can +quite believe him, he has bursts of zeal, in which he abandons his more +urgent occupation and resumes the search. + + + + Whether or not it will remain lost for ever, with the material and +origin of it, are things equally speculative at the present time. If the +present purchaser is a collector, one would have expected the enquiries +of Mr. Wace to have readied him through the dealers. He has been able to +discover Mr. Cave's clergyman and "Oriental" -- no other than the Rev. +James Parker and the young Prince of Bosso-Kuni in Java. I am obliged to +them for certain particulars. The object of the Prince was simply +curiosity -- and extravagance. He was so eager to buy, +because Cave was so oddly reluctant to sell. It is just as possible that +the buyer in the second instance was simply a casual purchaser and not a +collector at all, and the crystal egg, for all I know, may at the +present moment be within a mile of me, decorating a drawing-room or +serving as a paper-weight -- its remarkable functions all unknown. +Indeed, it is partly with the idea of such a possibility that I have +thrown this narrative into a form that will give it a chance of being +read by the ordinary consumer of fiction. + + + + My own ideas in the matter are practically identical with those of +Mr. Wace. I believe the crystal on the mast in Mars and the crystal egg +of Mr. Cave's to be in some physical, but at present quite inexplicable, +way en rapport, and we both believe further that the terrestrial crystal +must have been -- possibly at some remote date -- sent hither from that +planet, in order to give the Martians a near view of our affairs. +Possibly the fellows to the crystals in the other masts are also on our +globe. No theory of hallucination suffices for the facts. + + + + + + diff --git a/testfiles/data/doc2html.xsl b/testfiles/data/doc2html.xsl new file mode 100644 index 0000000..9a98c23 --- /dev/null +++ b/testfiles/data/doc2html.xsl @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + +

+ +

+ + + +

+ + + +

+
+ + + +
+ +
+ + +

+ +

+
+ + diff --git a/testfiles/data/example-FEXTRA-FCOMMENT.gz b/testfiles/data/example-FEXTRA-FCOMMENT.gz new file mode 100644 index 0000000..69ee90c Binary files /dev/null and b/testfiles/data/example-FEXTRA-FCOMMENT.gz differ diff --git a/testfiles/data/visual-bounds.svg b/testfiles/data/visual-bounds.svg new file mode 100644 index 0000000..560607a --- /dev/null +++ b/testfiles/data/visual-bounds.svg @@ -0,0 +1,665 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + Rafael Siejakowski + + + + + + + + + + + + + + + + + + + + + + + + + + For each of the objects and groups below, the gray rectangle represents the expected visual bounds. + Objects participating in the test have IDs "obj-%d" and their visual bounds "vbb-%d" where %d is an + integer between 1 and (active field): + 13 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Clipped translated group + Clipped path + Blurred shape + Blurred group + Transformed group + Simple group + Group containing a nested transformed group + ← TODO: this test is disabled because it currently (1.2.2 release) fails!Fix it and add more others like it! + Transformed rectangle + + + + + Clippedgroup + Usage of symbol with viewBox + + Usage of symbol without viewBox + + diff --git a/testfiles/doc-per-case-test.cpp b/testfiles/doc-per-case-test.cpp new file mode 100644 index 0000000..c69fafe --- /dev/null +++ b/testfiles/doc-per-case-test.cpp @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Test fixture with SPDocument per entire test case. + * + * Author: + * Jon A. Cruz + * + * Copyright (C) 2015 Authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include "doc-per-case-test.h" + +#include "inkscape.h" + +std::unique_ptr DocPerCaseTest::_doc = nullptr; + +DocPerCaseTest::DocPerCaseTest() : + ::testing::Test() +{ +} + +void DocPerCaseTest::SetUpTestCase() +{ + if ( !Inkscape::Application::exists() ) + { + // Create the global inkscape object. + Inkscape::Application::create(false); + } + + _doc.reset(SPDocument::createNewDoc( NULL, TRUE, true )); + ASSERT_TRUE(bool(_doc)); +} + +void DocPerCaseTest::TearDownTestCase() +{ + _doc.reset(); +} + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/testfiles/doc-per-case-test.h b/testfiles/doc-per-case-test.h new file mode 100644 index 0000000..aa25280 --- /dev/null +++ b/testfiles/doc-per-case-test.h @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Test fixture with SPDocument per entire test case. + * + * Author: + * Jon A. Cruz + * + * Copyright (C) 2015 Authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include "gtest/gtest.h" + +#include "document.h" + + +/** + * Simple fixture that creates a single SPDocument to be shared between all tests + * in this test case. + */ +class DocPerCaseTest : public ::testing::Test +{ +public: + DocPerCaseTest(); + +protected: + static void SetUpTestCase(); + + static void TearDownTestCase(); + + static std::unique_ptr _doc; +}; + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/testfiles/fuzzer.cpp b/testfiles/fuzzer.cpp new file mode 100644 index 0000000..2eee151 --- /dev/null +++ b/testfiles/fuzzer.cpp @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** @file + * TODO: insert short description here + *//* + * Authors: see git history + * + * Copyright (C) 2017 Authors + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ +#include "xml/repr.h" +#include "inkscape.h" +#include "document.h" + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + g_type_init(); + Inkscape::GC::init(); + if ( !Inkscape::Application::exists() ) + Inkscape::Application::create(false); + //void* a= sp_repr_read_mem((const char*)data, size, 0); + auto doc = std::unique_ptr(SPDocument::createNewDocFromMem((const char *)data, size, 0)); + return 0; +} diff --git a/testfiles/fuzzer.dict b/testfiles/fuzzer.dict new file mode 100644 index 0000000..c746484 --- /dev/null +++ b/testfiles/fuzzer.dict @@ -0,0 +1,526 @@ +# Dictionary for the fuzzer to "guess" faster important words. +# Contains xml keywords and svg element names and attributes. +# It might be useful to remove some of them, maybe. +# SPDX-License-Identifier: GPL-2.0-or-later + +"100" +"200" +"300" +"400" +"500" +"600" +"700" +"800" +"900" +"a" +"accent-height" +"accumulate" +"additive" +"after-edge" +"alignment-baseline" +"all" +"alphabetic" +"altGlyph" +"altGlyphDef" +"altGlyphItem" +"amplitude" +"animate" +"animateColor" +"animateMotion" +"animateTransform" +"arabic-form" +"ascent" +attr_encoding=" encoding=\"1\"" +attr_generic=" a=\"1\"" +attr_href=" href=\"1\"" +"attributeName" +"attributeType" +attr_standalone=" standalone=\"no\"" +attr_version=" version=\"1\"" +attr_xml_base=" xml:base=\"1\"" +attr_xml_id=" xml:id=\"1\"" +attr_xml_lang=" xml:lang=\"1\"" +attr_xmlns=" xmlns=\"1\"" +attr_xml_space=" xml:space=\"1\"" +"auto" +"azimuth" +"baseFrequency" +"baseline" +"baseline-shift" +"baseProfile" +"bbox" +"before-edge" +"begin" +"bevel" +"bias" +"bidi-override" +"blink" +"block" +"bold" +"bolder" +"butt" +"by" +"calcMode" +"cap-height" +"caption" +"central" +"circle" +"class" +"clip" +"clip-path" +"clipPath" +"clipPathUnits" +"clip-rule" +"collapse" +"color" +"color-interpolation" +"color-interpolation-filters" +"color-profile" +"color-rendering" +"compact" +"condensed" +"contentScriptType" +"contentStyleType" +"crispEdges" +"crosshair" +"currentColor" +"cursor" +"cx" +"cy" +"d" +"default" +"defs" +"desc" +"descent" +"diffuseConstant" +"direction" +"display" +"divisor" +"dominant-baseline" +"dur" +"dx" +"dy" +"edgeMode" +"elevation" +"ellipse" +"embed" +"enable-background" +"end" +entity_builtin="<" +entity_decimal="" +entity_external="&a;" +entity_hex="" +"e-resize" +"evenodd" +"expanded" +"exponent" +"externalResourcesRequired" +"extra-condensed" +"extra-expanded" +"feBlend" +"feColorMatrix" +"feComponentTransfer" +"feComposite" +"feConvolveMatrix" +"feDiffuseLighting" +"feDisplacementMap" +"feDistantLight" +"feFlood" +"feFuncA" +"feFuncB" +"feFuncG" +"feFuncR" +"feGaussianBlur" +"feImage" +"feMerge" +"feMergeNode" +"feMorphology" +"feOffset" +"fePointLight" +"feSpecularLighting" +"feSpotLight" +"feTile" +"feTurbulence" +"fill" +"fill-opacity" +"fill-rule" +"filter" +"filterRes" +"filterUnits" +"flood-color" +"flood-opacity" +"font" +"font-face" +"font-face-format" +"font-face-name" +"font-face-src" +"font-face-uri" +"font-family" +"font-size" +"font-size-adjust" +"font-stretch" +"font-style" +"font-variant" +"font-weight" +"foreignObject" +"format" +"from" +"fx" +"fy" +"g" +"g1" +"g2" +"geometricPrecision" +"glyph" +"glyph-name" +"glyph-orientation-horizontal" +"glyph-orientation-vertical" +"glyphRef" +"gradientTransform" +"gradientUnits" +"hanging" +"height" +"help" +"hidden" +"hkern" +"horiz-adv-x" +"horiz-origin-x" +"horiz-origin-y" +"icon" +"id" +"ideographic" +"image" +"image-rendering" +"in" +"in2" +"individual" +"inherit" +"inline" +"inline-table" +"intercept" +"italic" +"k" +"k1" +"k2" +"k3" +"k4" +"kernelMatrix" +"kernelUnitLength" +"kerning" +"keyPoints" +"keySplines" +"keyTimes" +"lang" +"lengthAdjust" +"letter-spacing" +"lighter" +"lighting-color" +"limitingConeAngle" +"line" +"linearGradient" +"linearRGB" +"'line-height'" +"line-through" +"list-item" +"local" +"lr" +"lr-tb" +"ltr" +"marker" +"marker-end" +"markerHeight" +"marker-mid" +"marker-start" +"markerUnits" +"markerWidth" +"mask" +"maskContentUnits" +"maskUnits" +"mathematical" +"max" +"media" +"menu" +"message-box" +"metadata" +"method" +"middle" +"min" +"missing-glyph" +"miter" +"mode" +"move" +"mpath" +"name" +"narrower" +"ne-resize" +"new" +"no-change" +"none" +"nonzero" +"normal" +"n-resize" +"numOctaves" +"nw-resize" +"oblique" +"offset" +"onabort" +"onactivate" +"onbegin" +"onclick" +"onend" +"onerror" +"onfocusin" +"onfocusout" +"onload" +"onmousedown" +"onmousemove" +"onmouseout" +"onmouseover" +"onmouseup" +"onrepeat" +"onresize" +"onscroll" +"onunload" +"onzoom" +"opacity" +"operator" +"optimizeLegibility" +"optimizeQuality" +"optimizeSpeed" +"order" +"orient" +"orientation" +"origin" +"overflow" +"overline" +"overline-position" +"overline-thickness" +"paint" +"painted" +"panose-1" +"path" +"pathLength" +"pattern" +"patternContentUnits" +"patternTransform" +"patternUnits" +"pointer" +"pointer-events" +"points" +"pointsAtX" +"pointsAtY" +"pointsAtZ" +"polygon" +"polyline" +"preserveAlpha" +"preserveAspectRatio" +"primitiveUnits" +"properties" +"r" +"radialGradient" +"radius" +"rect" +"refX" +"refY" +"rendering-intent" +"repeatCount" +"repeatDur" +"requiredExtensions" +"requiredFeatures" +"reset-size" +"restart" +"result" +"rl" +"rl-tb" +"rotate" +"round" +"rtl" +"run-in" +"rx" +"ry" +"scale" +"script" +"scroll" +"see" +"seed" +"semi-condensed" +"semi-expanded" +"se-resize" +"set" +"shape-rendering" +"slope" +"small-caps" +"small-caption" +"spacing" +"Specifying" +"specularConstant" +"specularExponent" +"spreadMethod" +"square" +"s-resize" +"sRGB" +"start" +"startOffset" +"status-bar" +"stdDeviation" +"stemh" +"stemv" +"stitchTiles" +"stop" +"stop-color" +"stop-opacity" +"strikethrough-position" +"strikethrough-thickness" +"string" +string_any="ANY" +string_brackets="[]" +string_cdata="CDATA" +string_col_fallback=":fallback" +string_col_generic=":a" +string_col_include=":include" +string_dashes="--" +string_empty_dblquotes="\"\"" +string_empty="EMPTY" +string_empty_quotes="''" +string_entities="ENTITIES" +string_entity="ENTITY" +string_fixed="#FIXED" +string_id="ID" +string_idref="IDREF" +string_idrefs="IDREFS" +string_implied="#IMPLIED" +string_nmtoken="NMTOKEN" +string_nmtokens="NMTOKENS" +string_notation="NOTATION" +string_parentheses="()" +string_pcdata="#PCDATA" +string_percent="%a" +string_public="PUBLIC" +string_required="#REQUIRED" +string_schema=":schema" +string_system="SYSTEM" +string_ucs4="UCS-4" +string_utf16="UTF-16" +string_utf8="UTF-8" +string_xmlns="xmlns:" +"stroke" +"stroke-dasharray" +"stroke-dashoffset" +"stroke-linecap" +"stroke-linejoin" +"stroke-miterlimit" +"stroke-opacity" +"stroke-width" +"style" +"sub" +"super" +"surfaceScale" +"svg" +"switch" +"sw-resize" +"symbol" +"systemLanguage" +"table" +"table-caption" +"table-cell" +"table-column" +"table-column-group" +"table-footer-group" +"table-header-group" +"table-row" +"table-row-group" +"tableValues" +tag_attlist="" +tag_doctype="" +tag_open_close="
" +tag_open_exclamation="" +tag_xml_q="" +"target" +"targetX" +"targetY" +"tb" +"tb-rl" +"text" +"text-after-edge" +"text-anchor" +"text-before-edge" +"text-decoration" +"textLength" +"textPath" +"text-rendering" +"title" +"to" +"transform" +"tref" +"tspan" +"type" +"u1" +"u2" +"ultra-condensed" +"ultra-expanded" +"underline" +"underline-position" +"underline-thickness" +"unicode" +"unicode-bidi" +"unicode-range" +"units-per-em" +"use" +"use-script" +"v-alphabetic" +"values" +"version" +"vert-adv-y" +"vert-origin-x" +"vert-origin-y" +"v-hanging" +"v-ideographic" +"view" +"viewBox" +"viewTarget" +"visibility" +"visible" +"visibleFill" +"visiblePainted" +"visibleStroke" +"vkern" +"v-mathematical" +"wait" +"wider" +"width" +"widths" +"word-spacing" +"w-resize" +"writing-mode" +"x" +"x1" +"x2" +"xChannelSelector" +"x-height" +"xlink:actuate" +"xlink:arcrole" +"xlink:href" +"xlink:role" +"xlink:show" +"xlink:title" +"xlink:type" +#XML +"xml:base" +"xml:lang" +"xml:space" +"y" +"y1" +"y2" +"yChannelSelector" +"z" +"zoomAndPan" diff --git a/testfiles/lpe_tests/CMakeLists.txt b/testfiles/lpe_tests/CMakeLists.txt new file mode 100644 index 0000000..d79da39 --- /dev/null +++ b/testfiles/lpe_tests/CMakeLists.txt @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +file(GLOB _FILES "README" "*.svg") +set(${_FILES}) \ No newline at end of file diff --git a/testfiles/lpe_tests/Inkscape_0_92.svg b/testfiles/lpe_tests/Inkscape_0_92.svg new file mode 100755 index 0000000..b77d78f --- /dev/null +++ b/testfiles/lpe_tests/Inkscape_0_92.svg @@ -0,0 +1,1729 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/testfiles/lpe_tests/Inkscape_0_92_64.svg b/testfiles/lpe_tests/Inkscape_0_92_64.svg new file mode 100755 index 0000000..38ca798 --- /dev/null +++ b/testfiles/lpe_tests/Inkscape_0_92_64.svg @@ -0,0 +1,516 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/testfiles/lpe_tests/Inkscape_1_0.svg b/testfiles/lpe_tests/Inkscape_1_0.svg new file mode 100644 index 0000000..0f3fd02 --- /dev/null +++ b/testfiles/lpe_tests/Inkscape_1_0.svg @@ -0,0 +1,6137 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + shape + path + group + nested group + open + 1.0 LPE TESTS + + + + attach path + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/testfiles/lpe_tests/Inkscape_1_0_64.svg b/testfiles/lpe_tests/Inkscape_1_0_64.svg new file mode 100644 index 0000000..c67534b --- /dev/null +++ b/testfiles/lpe_tests/Inkscape_1_0_64.svg @@ -0,0 +1,2966 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + shape + path + group + nested group + nested with LPE inside + staking tests + open + 1.0 LPE TESTS + + + + + + + + + + + + + + + + + + + + + + + lpe name + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/testfiles/lpe_tests/Inkscape_1_1.svg b/testfiles/lpe_tests/Inkscape_1_1.svg new file mode 100644 index 0000000..b5f0b37 --- /dev/null +++ b/testfiles/lpe_tests/Inkscape_1_1.svg @@ -0,0 +1,18291 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + envelope deformation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 406.59px + + + + 391.66px + + + + 15.43px + + + + 11.45px + + + + 63.35px + + + + 43.49px + + + + 39.15px + + + + 500.42px + + + + 251.60px + + + + 64.83mm + + + + 135.54mm + + + + 134.83mm + + + + 38.79mm + + + + 89.13mm + + + + 98.65mm + + + + 77.40mm + + + diff --git a/testfiles/lpe_tests/Inkscape_1_2.svg b/testfiles/lpe_tests/Inkscape_1_2.svg new file mode 100644 index 0000000..f62d8a3 --- /dev/null +++ b/testfiles/lpe_tests/Inkscape_1_2.svg @@ -0,0 +1,22065 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + envelope deformation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 406.59px + + + + 391.66px + + + + 15.43px + + + + 11.45px + + + + 63.35px + + + + 43.49px + + + + 39.15px + + + + 500.42px + + + + 251.60px + + + + 89.13mm + + + + 98.65mm + + + + 77.40mm + + + + 64.83mm + + + + 135.54mm + + + + 134.83mm + + + + 38.79mm + + + diff --git a/testfiles/lpe_tests/Inkscape_1_3.svg b/testfiles/lpe_tests/Inkscape_1_3.svg new file mode 100644 index 0000000..13e043b --- /dev/null +++ b/testfiles/lpe_tests/Inkscape_1_3.svg @@ -0,0 +1,22404 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + shape + path + group + nested group + nested with LPE inside + staking tests + open + 1.3 LPE TESTS + + + + + + + + + + + + + + + + + + + + + + + + lpe name + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + tiling + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + von kohtch + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + transform 2 points + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + tapper stroke + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + stich sub-paths + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + spiro + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + slice + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + sketch + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + simplify + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + show handles + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + rulers + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + lpe name + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + rotate copies + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + power stroke + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + power mask + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + power clip + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + perspective envelope + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + pattern along path + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + offset + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + mirror symmetry + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + measure segments + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + lattice deformation II + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + knot + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + join type + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + interpolate paths + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + interpolate points + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + rough hatches + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + gears + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + fill between strokes + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + fill between many + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ellipse by 5 points + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + envelope deformation + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ellipse from points + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + dashed stroke + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + corners + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + construct_grid + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + clone_original + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bspline + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + boundingbox + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bool + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bend + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + attach path + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 64.83mm + + + + 135.54mm + + + + 134.83mm + + + + 38.79mm + + + + 89.13mm + + + + 98.65mm + + + + 77.40mm + + + + 406.59px + + + + 391.66px + + + + 15.43px + + + + 11.45px + + + + 63.35px + + + + 43.49px + + + + 39.15px + + + + 500.42px + + + + 251.60px + + + diff --git a/testfiles/lpe_tests/README b/testfiles/lpe_tests/README new file mode 100644 index 0000000..c5521b0 --- /dev/null +++ b/testfiles/lpe_tests/README @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +This folder contains LPE to test. LPE test API has some parameters that can be helpful for fixing tests, especially legacy ones. +* inkscape:test-threshold="0.3" in the root SVG element set a precision to all the tests. If it's in a single element, add to it. If it doesn't exist, use a default one 0.0001 +* inkscape:test-ignore="true" skip this element from testing +The method to create/refresh a LPE version test need update all LPE in document +THe only way I found work properly for ALL inlude legacy is: +1) Select Layer LPEs in layers and objects dialog +2) ungroup +3) group again +4) rename new layer to LPEs +5) delete all no layers objects at root level (become recreated) diff --git a/testfiles/lpespaths-test.h b/testfiles/lpespaths-test.h new file mode 100644 index 0000000..b2065b0 --- /dev/null +++ b/testfiles/lpespaths-test.h @@ -0,0 +1,235 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** @file + * LPE test file wrapper + *//* + * Authors: see git history + * + * Copyright (C) 2020 Authors + * + * Released under GNU GPL version 2 or later, read the file 'COPYING' for more information + */ + +#include <2geom/pathvector.h> +#include +#include +#include +#include +#include + +#include "src/extension/init.h" +#include "inkscape-application.h" +#include "src/util/numeric/converters.h" + +using namespace Inkscape; + +/* This class allow test LPE's. To make possible in latest release of Inkscape + * LPE is not updated on load (if in the future any do we must take account) so we load + * a svg, get all "d" attribute from paths, shapes... + * Update all path effects with root object and check equality of paths. + * We use some helpers inside the SVG document to test: + * inkscape:test-threshold="0.1" can be global using in root element or per item + * inkscape:test-ignore="1" ignore this element from tests + * Question: Maybe is better store SVG as files instead inline CPP files, there is a + * 1.2 started MR, I can't finish without too much work than a cmake advanced user + */ + +class LPESPathsTest : public ::testing::Test +{ +protected: + void SetUp() override + { + // setup hidden dependency + Application::create(false); + Inkscape::Extension::init(); + const testing::TestInfo* const test_info = + testing::UnitTest::GetInstance()->current_test_info(); + svg = test_info->file();; +#ifdef INKSCAPE_TESTS_DIR + svg = INKSCAPE_TESTS_DIR; +#else + size_t pos = svg.find("lpespaths-test.h"); + svg.erase(pos); +#endif + svg += "/lpe_tests/"; // gitlab use this separator + /* svg += test_info->test_suite_name(); */ + svg += test_info->name(); + svg += ".svg"; + } + + void pathCompare(char const *a, char const *b, Glib::ustring const &id, double precision = 0.001) + { + bool success = false; + pathCompareInternal(a, b, precision, success); + if (!success) { + #define DIAG(x) " " << #x << ":\n " << x << std::endl + std::cout << "More information about the failure:\n" << DIAG(svg) << DIAG(id) << DIAG(a) << DIAG(b); + failed.emplace_back(id); + } + } + + void pathCompareInternal(char const *a, char const *b, double precision, bool &success) + { + bool a_empty = a[0] == '\0'; + bool b_empty = b[0] == '\0'; + if (a_empty || b_empty) { + if (a_empty && b_empty) { + success = true; + return; + } else { + FAIL() << "Mismatching emptiness of paths"; + } + } + + Geom::PathVector apv = sp_svg_read_pathv(a); + Geom::PathVector bpv = sp_svg_read_pathv(b); + if (apv.empty()) { + FAIL() << "Couldn't parse original 'd'"; + } + if (bpv.empty()) { + FAIL() << "Couldn't parse 'd'"; + } + + size_t totala = apv.curveCount(); + size_t totalb = bpv.curveCount(); + ASSERT_EQ(totala, totalb); + + // find initial + size_t initial = 0; + for (size_t i = 0; i < totala; i++) { + Geom::Point pointa = apv.pointAt(0.0); + Geom::Point pointb = bpv.pointAt(i); + if (Geom::are_near(pointa, pointb, precision)) { + initial = i; + break; + } + } + + if (initial != 0 && initial == totala) { + std::cout << "[ WARN ] Curve reversed. We do not block here. We reverse the path and test node positions on reverse" << std::endl; + bpv.reverse(); + } else if (initial != 0) { + std::cout << "[ WARN ] Different starting node. We do not block here. We gap the origin to " << initial << " de " << totala << " and test with the pathvector reindexed" << std::endl; + } + + for (size_t i = 0; i < totala; i++) { + if (initial >= totala) { + initial = 0; + } + Geom::Point pointa = apv.pointAt(i + 0.2); + Geom::Point pointb = bpv.pointAt(initial + 0.2); + Geom::Point pointc = apv.pointAt(i + 0.4); + Geom::Point pointd = bpv.pointAt(initial + 0.4); + Geom::Point pointe = apv.pointAt(i); + Geom::Point pointf = bpv.pointAt(initial); + ASSERT_NEAR(pointa[Geom::X], pointb[Geom::X], precision); + ASSERT_NEAR(pointa[Geom::Y], pointb[Geom::Y], precision); + ASSERT_NEAR(pointc[Geom::X], pointd[Geom::X], precision); + ASSERT_NEAR(pointc[Geom::Y], pointd[Geom::Y], precision); + ASSERT_NEAR(pointe[Geom::X], pointf[Geom::X], precision); + ASSERT_NEAR(pointe[Geom::Y], pointf[Geom::Y], precision); + initial++; + } + + success = true; + } + + void TearDown() override + { + Glib::ustring ids = ""; + for (auto fail : failed) { + if (ids != "") { + ids += ","; + } + ids += fail; + } + if (ids != "") { + FAIL() << "[FAILED IDS] " << ids; + } + } + + // you can override custom threshold from svg file using in + // root svg from global and override with per shape "inkscape:test-threshold" + void testDoc(std::string file) + { + double precision = 0.001; + auto doc = SPDocument::createNewDoc(file.c_str(), false); + ASSERT_TRUE(doc != nullptr); + SPLPEItem *lpeitem = doc->getRoot(); + std::vector objs; + std::vector ids; + std::vector lpes; + std::vector ds; + for (auto obj : doc->getObjectsByElement("path")) { + objs.push_back(obj); + } + for (auto obj : doc->getObjectsByElement("ellipse")) { + objs.push_back(obj); + } + for (auto obj : doc->getObjectsByElement("circle")) { + objs.push_back(obj); + } + for (auto obj : doc->getObjectsByElement("rect")) { + objs.push_back(obj); + } + for (auto obj : objs) { + SPObject *parentobj = obj->parent; + SPObject *layer = obj; + while (parentobj->parent && parentobj->parent->parent) { + layer = parentobj; + parentobj = parentobj->parent; + } + if (!g_strcmp0(obj->getAttribute("d"), "M 0,0")) { + if (obj->getAttribute("id")) { + std::cout << "[ WARN ] Item with id:" << obj->getAttribute("id") << " has empty path data" << std::endl; + } + } else if (!layer->getAttribute("inkscape:test-ignore") && obj->getAttribute("d") && obj->getAttribute("id")) { + ds.push_back(Glib::ustring(obj->getAttribute("d"))); + ids.push_back(Glib::ustring(obj->getAttribute("id"))); + lpes.push_back(Glib::ustring(layer->getAttribute("inkscape:label") ? layer->getAttribute("inkscape:label") : layer->getAttribute("id"))); + } + } + sp_file_fix_lpe(doc); + doc->ensureUpToDate(); + sp_lpe_item_update_patheffect(lpeitem, true, true, true); + // to bypass onload + sp_lpe_item_update_patheffect(lpeitem, true, true, true); + if (lpeitem->getAttribute("inkscape:test-threshold")) { + precision = Util::read_number(lpeitem->getAttribute("inkscape:test-threshold")); + } + size_t index = 0; + for (auto id : ids) { + SPObject *obj = doc->getObjectById(id); + if (obj) { + if (obj->getAttribute("inkscape:test-threshold")) { + precision = Util::read_number(obj->getAttribute("inkscape:test-threshold")); + } + if (!obj->getAttribute("inkscape:test-ignore")) { + Glib::ustring idandlayer = ""; + idandlayer = obj->getAttribute("id"); + idandlayer += "("; // top layers has the LPE name tested in in id + idandlayer += lpes[index]; + idandlayer += ")"; + pathCompare(ds[index].c_str(), obj->getAttribute("d"), idandlayer , precision); + } else { + std::cout << "[ WARN ] Item with id:" << obj->getAttribute("id") << " ignored by inkscape:test-ignore" << std::endl; + } + } else { + std::cout << "[ WARN ] Item with id:" << id << " removed on apply LPE" << std::endl; + } + index++; + } + } + std::string svg = ""; + std::vector failed; +}; + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/testfiles/rendering_tests/CMakeLists.txt b/testfiles/rendering_tests/CMakeLists.txt new file mode 100644 index 0000000..c081440 --- /dev/null +++ b/testfiles/rendering_tests/CMakeLists.txt @@ -0,0 +1,70 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +function(add_rendering_test rendering_test) + cmake_parse_arguments(ARG "" "FUZZ;DISABLE" "" ${ARGN}) + set(testname "render_${rendering_test}") + add_test(NAME ${testname} + COMMAND bash ${CMAKE_CURRENT_SOURCE_DIR}/test.sh ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/inkscape ${CMAKE_CURRENT_SOURCE_DIR}/${rendering_test} ${ARG_FUZZ} + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/testfiles/rendering_tests) + set_tests_properties(${testname} PROPERTIES ENVIRONMENT "${INKSCAPE_TEST_PROFILE_DIR_ENV}/${testname};${CMAKE_CTEST_ENV}") + if(${ARG_DISABLE}) + set_tests_properties(${testname} PROPERTIES DISABLED "True") + endif() +endfunction() + +find_package(PkgConfig REQUIRED) +pkg_search_module(HARFBUZZ REQUIRED harfbuzz) + +# Add your test here (do not put the .svg extension) + +# -- Generic tests -- +add_rendering_test(test-empty) +add_rendering_test(test-dont-crash) +add_rendering_test(test-use FUZZ 0.03) + +# -- Selector tests -- +add_rendering_test(selector-important-002) +add_rendering_test(selector-important-003) + +add_rendering_test(multi-style) +add_rendering_test(style-parsing) + +# -- Text tests -- +## Many (if not all) of these tests are sensitive to the text rendering stack: FreeType, HarfBuzz, Pango. +add_rendering_test(text-flow FUZZ 0.5) + +# add_rendering_test(test-baseline-shift) +## Small differences with code adapted for Pango 1.44. + +add_rendering_test(test-glyph-y-pos FUZZ 0.1) +## to be fixed since an update happened between harfbuzz 1.5.1(OK) and 1.6.0(FAIL). +## If you re-enable the test, you may have to *slightly* fix the expected rendering (hoping the fix happens upstream). +## Please also check that the rendering with harfbuzz <=1.5.1 is not *too* wrong (for older systems) +## cf Tav's post : https://www.patreon.com/posts/into-sinkhole-19021727 +## and bug https://bugzilla.gnome.org/show_bug.cgi?id=787526 + +# add_rendering_test(text-shaping) +## Expected rendering generated with Pango 1.44. Currently fails with +## CI as CI uses Pango 1.40. Enable after updating CI to Ubuntu 20.04. + +add_rendering_test(text-glyphs-combining FUZZ 0.1) +## Expected rendering generated with Pango 1.44. + +add_rendering_test(text-glyphs-vertical FUZZ 0.1) +## Expected rendering generated with Pango 1.44. + +# -- LPE tests -- +add_rendering_test(test-powerstroke-join) + +# geometric properties (SVG 2.0 feature) +add_rendering_test(symbol-svg2-geometry-properties) +add_rendering_test(symbol-svg2-ref) + +# Tests to run for 64-bit builds only. These fail in 32-bit builds (possibly due to rounding issues) +# TODO: Figure out actual cause and see if we can fix it +if(${CMAKE_SIZEOF_VOID_P} EQUAL 8) + add_rendering_test(test-rtl-vertical FUZZ 0.1) + + # .otf font with compressed SVG glyphs + add_rendering_test(text-gzipped-svg-glyph FUZZ 0.03) +endif() diff --git a/testfiles/rendering_tests/README b/testfiles/rendering_tests/README new file mode 100644 index 0000000..6ebcb4c --- /dev/null +++ b/testfiles/rendering_tests/README @@ -0,0 +1,26 @@ +HOWTO + +# Add a rendering test: + - create the svg file + - 0.92: + - inkscape .svg -d 96 -e expected_rendering/.png + - inkscape .svg -d 384 -e expected_rendering/-large.png + - 1.0: + - inkscape -d 96 --export-filename=expected_rendering/.png .svg + - inkscape -d 384 --export-filename=expected_rendering/-large.png .svg + - add the test in CMakeLists.txt + - use stable if possible to generate the reference png files + - git add .svg expected_rendering/-large.png expected_rendering/.png + +# Fix a failing test (due to a change in code): + - DO *NOT* MODIFY the expected rendering (or the svg) before getting advice from inkscape-devel@ + - fix your code if possible + - IF you change introduces a greater compatibility with css or browsers + - AND you cannot reasonably "update" files from older versions to match the appearance + - AND inkscape-devel@ has a consensus that it's the only way + -> do as you must + - manually double check the changes + +# Fix a failing test (due to a change in pixman or cairo): + - update renderings. Use a *stable* version to generate the renderings, NOT TRUNK + - manually check appearances diff --git a/testfiles/rendering_tests/drawing-pattern-test.svg b/testfiles/rendering_tests/drawing-pattern-test.svg new file mode 100644 index 0000000..7e82a95 --- /dev/null +++ b/testfiles/rendering_tests/drawing-pattern-test.svg @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + diff --git a/testfiles/rendering_tests/expected_rendering/multi-style.png b/testfiles/rendering_tests/expected_rendering/multi-style.png new file mode 100644 index 0000000..7c7c2fb Binary files /dev/null and b/testfiles/rendering_tests/expected_rendering/multi-style.png differ diff --git a/testfiles/rendering_tests/expected_rendering/selector-important-002-large.png b/testfiles/rendering_tests/expected_rendering/selector-important-002-large.png new file mode 100644 index 0000000..e92eef0 Binary files /dev/null and b/testfiles/rendering_tests/expected_rendering/selector-important-002-large.png differ diff --git a/testfiles/rendering_tests/expected_rendering/selector-important-002.png b/testfiles/rendering_tests/expected_rendering/selector-important-002.png new file mode 100644 index 0000000..b0af9bd Binary files /dev/null and b/testfiles/rendering_tests/expected_rendering/selector-important-002.png differ diff --git a/testfiles/rendering_tests/expected_rendering/selector-important-003-large.png b/testfiles/rendering_tests/expected_rendering/selector-important-003-large.png new file mode 100644 index 0000000..91cb3af Binary files /dev/null and b/testfiles/rendering_tests/expected_rendering/selector-important-003-large.png differ diff --git a/testfiles/rendering_tests/expected_rendering/selector-important-003.png b/testfiles/rendering_tests/expected_rendering/selector-important-003.png new file mode 100644 index 0000000..dfe3dbc Binary files /dev/null and b/testfiles/rendering_tests/expected_rendering/selector-important-003.png differ diff --git a/testfiles/rendering_tests/expected_rendering/style-parsing.png b/testfiles/rendering_tests/expected_rendering/style-parsing.png new file mode 100644 index 0000000..5b60834 Binary files /dev/null and b/testfiles/rendering_tests/expected_rendering/style-parsing.png differ diff --git a/testfiles/rendering_tests/expected_rendering/symbol-svg2-geometry-properties.png b/testfiles/rendering_tests/expected_rendering/symbol-svg2-geometry-properties.png new file mode 100644 index 0000000..ca5e83e Binary files /dev/null and b/testfiles/rendering_tests/expected_rendering/symbol-svg2-geometry-properties.png differ diff --git a/testfiles/rendering_tests/expected_rendering/symbol-svg2-ref.png b/testfiles/rendering_tests/expected_rendering/symbol-svg2-ref.png new file mode 100644 index 0000000..5892726 Binary files /dev/null and b/testfiles/rendering_tests/expected_rendering/symbol-svg2-ref.png differ diff --git a/testfiles/rendering_tests/expected_rendering/test-baseline-shift-large.png b/testfiles/rendering_tests/expected_rendering/test-baseline-shift-large.png new file mode 100644 index 0000000..29369a8 Binary files /dev/null and b/testfiles/rendering_tests/expected_rendering/test-baseline-shift-large.png differ diff --git a/testfiles/rendering_tests/expected_rendering/test-baseline-shift.png b/testfiles/rendering_tests/expected_rendering/test-baseline-shift.png new file mode 100644 index 0000000..45aed90 Binary files /dev/null and b/testfiles/rendering_tests/expected_rendering/test-baseline-shift.png differ diff --git a/testfiles/rendering_tests/expected_rendering/test-dont-crash.png b/testfiles/rendering_tests/expected_rendering/test-dont-crash.png new file mode 100644 index 0000000..a2d005e Binary files /dev/null and b/testfiles/rendering_tests/expected_rendering/test-dont-crash.png differ diff --git a/testfiles/rendering_tests/expected_rendering/test-empty-large.png b/testfiles/rendering_tests/expected_rendering/test-empty-large.png new file mode 100644 index 0000000..34acf1f Binary files /dev/null and b/testfiles/rendering_tests/expected_rendering/test-empty-large.png differ diff --git a/testfiles/rendering_tests/expected_rendering/test-empty.png b/testfiles/rendering_tests/expected_rendering/test-empty.png new file mode 100644 index 0000000..2e0a5fe Binary files /dev/null and b/testfiles/rendering_tests/expected_rendering/test-empty.png differ diff --git a/testfiles/rendering_tests/expected_rendering/test-glyph-y-pos-large.png b/testfiles/rendering_tests/expected_rendering/test-glyph-y-pos-large.png new file mode 100644 index 0000000..57066fe Binary files /dev/null and b/testfiles/rendering_tests/expected_rendering/test-glyph-y-pos-large.png differ diff --git a/testfiles/rendering_tests/expected_rendering/test-glyph-y-pos.png b/testfiles/rendering_tests/expected_rendering/test-glyph-y-pos.png new file mode 100644 index 0000000..38c0117 Binary files /dev/null and b/testfiles/rendering_tests/expected_rendering/test-glyph-y-pos.png differ diff --git a/testfiles/rendering_tests/expected_rendering/test-powerstroke-join-large.png b/testfiles/rendering_tests/expected_rendering/test-powerstroke-join-large.png new file mode 100644 index 0000000..72d8821 Binary files /dev/null and b/testfiles/rendering_tests/expected_rendering/test-powerstroke-join-large.png differ diff --git a/testfiles/rendering_tests/expected_rendering/test-powerstroke-join.png b/testfiles/rendering_tests/expected_rendering/test-powerstroke-join.png new file mode 100644 index 0000000..1eeec07 Binary files /dev/null and b/testfiles/rendering_tests/expected_rendering/test-powerstroke-join.png differ diff --git a/testfiles/rendering_tests/expected_rendering/test-rtl-vertical-large.png b/testfiles/rendering_tests/expected_rendering/test-rtl-vertical-large.png new file mode 100644 index 0000000..d473a59 Binary files /dev/null and b/testfiles/rendering_tests/expected_rendering/test-rtl-vertical-large.png differ diff --git a/testfiles/rendering_tests/expected_rendering/test-rtl-vertical.png b/testfiles/rendering_tests/expected_rendering/test-rtl-vertical.png new file mode 100644 index 0000000..49db6cb Binary files /dev/null and b/testfiles/rendering_tests/expected_rendering/test-rtl-vertical.png differ diff --git a/testfiles/rendering_tests/expected_rendering/test-use-large.png b/testfiles/rendering_tests/expected_rendering/test-use-large.png new file mode 100644 index 0000000..c1f4597 Binary files /dev/null and b/testfiles/rendering_tests/expected_rendering/test-use-large.png differ diff --git a/testfiles/rendering_tests/expected_rendering/test-use.png b/testfiles/rendering_tests/expected_rendering/test-use.png new file mode 100644 index 0000000..a995136 Binary files /dev/null and b/testfiles/rendering_tests/expected_rendering/test-use.png differ diff --git a/testfiles/rendering_tests/expected_rendering/text-flow.png b/testfiles/rendering_tests/expected_rendering/text-flow.png new file mode 100644 index 0000000..bef670d Binary files /dev/null and b/testfiles/rendering_tests/expected_rendering/text-flow.png differ diff --git a/testfiles/rendering_tests/expected_rendering/text-glyphs-combining-large.png b/testfiles/rendering_tests/expected_rendering/text-glyphs-combining-large.png new file mode 100644 index 0000000..5568888 Binary files /dev/null and b/testfiles/rendering_tests/expected_rendering/text-glyphs-combining-large.png differ diff --git a/testfiles/rendering_tests/expected_rendering/text-glyphs-combining.png b/testfiles/rendering_tests/expected_rendering/text-glyphs-combining.png new file mode 100644 index 0000000..32a6781 Binary files /dev/null and b/testfiles/rendering_tests/expected_rendering/text-glyphs-combining.png differ diff --git a/testfiles/rendering_tests/expected_rendering/text-glyphs-vertical-large.png b/testfiles/rendering_tests/expected_rendering/text-glyphs-vertical-large.png new file mode 100644 index 0000000..f49cbbd Binary files /dev/null and b/testfiles/rendering_tests/expected_rendering/text-glyphs-vertical-large.png differ diff --git a/testfiles/rendering_tests/expected_rendering/text-glyphs-vertical.png b/testfiles/rendering_tests/expected_rendering/text-glyphs-vertical.png new file mode 100644 index 0000000..cab1c0b Binary files /dev/null and b/testfiles/rendering_tests/expected_rendering/text-glyphs-vertical.png differ diff --git a/testfiles/rendering_tests/expected_rendering/text-gzipped-svg-glyph.png b/testfiles/rendering_tests/expected_rendering/text-gzipped-svg-glyph.png new file mode 100644 index 0000000..8e8a184 Binary files /dev/null and b/testfiles/rendering_tests/expected_rendering/text-gzipped-svg-glyph.png differ diff --git a/testfiles/rendering_tests/expected_rendering/text-shaping-large.png b/testfiles/rendering_tests/expected_rendering/text-shaping-large.png new file mode 100644 index 0000000..e84ebf5 Binary files /dev/null and b/testfiles/rendering_tests/expected_rendering/text-shaping-large.png differ diff --git a/testfiles/rendering_tests/expected_rendering/text-shaping.png b/testfiles/rendering_tests/expected_rendering/text-shaping.png new file mode 100644 index 0000000..346ab77 Binary files /dev/null and b/testfiles/rendering_tests/expected_rendering/text-shaping.png differ diff --git a/testfiles/rendering_tests/fonts/Estedad-Medium.ttf b/testfiles/rendering_tests/fonts/Estedad-Medium.ttf new file mode 100644 index 0000000..d4844c2 Binary files /dev/null and b/testfiles/rendering_tests/fonts/Estedad-Medium.ttf differ diff --git a/testfiles/rendering_tests/fonts/GeomTest-Regular.otf b/testfiles/rendering_tests/fonts/GeomTest-Regular.otf new file mode 100644 index 0000000..a008cbf Binary files /dev/null and b/testfiles/rendering_tests/fonts/GeomTest-Regular.otf differ diff --git a/testfiles/rendering_tests/fonts/GeomTest-gzipped-SVG-glyphs.otf b/testfiles/rendering_tests/fonts/GeomTest-gzipped-SVG-glyphs.otf new file mode 100755 index 0000000..d8a89e3 Binary files /dev/null and b/testfiles/rendering_tests/fonts/GeomTest-gzipped-SVG-glyphs.otf differ diff --git a/testfiles/rendering_tests/fonts/LICENSES b/testfiles/rendering_tests/fonts/LICENSES new file mode 100644 index 0000000..71bfc0e --- /dev/null +++ b/testfiles/rendering_tests/fonts/LICENSES @@ -0,0 +1,10 @@ + +All fonts in this directory are licensed under open licenses. + +Lohit (https://pagure.io/lohit SIL Open Font 1.1) +NotoSans (https://www.google.com/get/noto/ Open Font License 1.1) +Estedad (https://github.com/aminabedi68/Estedad/ SIL Open Font 1.1) + +GeomTest Released under Open Font Licens 1.1. Copyright Tavmjong Bah 2015,2019 + + diff --git a/testfiles/rendering_tests/fonts/Lohit-Telugu.ttf b/testfiles/rendering_tests/fonts/Lohit-Telugu.ttf new file mode 100644 index 0000000..3869703 Binary files /dev/null and b/testfiles/rendering_tests/fonts/Lohit-Telugu.ttf differ diff --git a/testfiles/rendering_tests/fonts/NotoSans-Regular.ttf b/testfiles/rendering_tests/fonts/NotoSans-Regular.ttf new file mode 100644 index 0000000..b031a49 Binary files /dev/null and b/testfiles/rendering_tests/fonts/NotoSans-Regular.ttf differ diff --git a/testfiles/rendering_tests/fonts/NotoSansCJKjp-Regular.otf b/testfiles/rendering_tests/fonts/NotoSansCJKjp-Regular.otf new file mode 100644 index 0000000..296fbeb Binary files /dev/null and b/testfiles/rendering_tests/fonts/NotoSansCJKjp-Regular.otf differ diff --git a/testfiles/rendering_tests/fonts/NotoSansHebrew-Regular.ttf b/testfiles/rendering_tests/fonts/NotoSansHebrew-Regular.ttf new file mode 100644 index 0000000..9bf03ab Binary files /dev/null and b/testfiles/rendering_tests/fonts/NotoSansHebrew-Regular.ttf differ diff --git a/testfiles/rendering_tests/multi-style-import-1.css b/testfiles/rendering_tests/multi-style-import-1.css new file mode 100644 index 0000000..e6fb1e0 --- /dev/null +++ b/testfiles/rendering_tests/multi-style-import-1.css @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/* overwritten later */ +rect { fill: red; } +.c1 { fill: red; } + +/* not overwritten */ +#background { fill: white; } +.c5 { fill: #00ccff; } diff --git a/testfiles/rendering_tests/multi-style-import-2.css b/testfiles/rendering_tests/multi-style-import-2.css new file mode 100644 index 0000000..3e0dd9b --- /dev/null +++ b/testfiles/rendering_tests/multi-style-import-2.css @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +rect { fill: blue; } +.c1 { fill: #990099; } + +/* overwritten later */ +.c4 { fill: red; } diff --git a/testfiles/rendering_tests/multi-style.svg b/testfiles/rendering_tests/multi-style.svg new file mode 100644 index 0000000..842f946 --- /dev/null +++ b/testfiles/rendering_tests/multi-style.svg @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/testfiles/rendering_tests/selector-important-002.svg b/testfiles/rendering_tests/selector-important-002.svg new file mode 100644 index 0000000..e5a66b6 --- /dev/null +++ b/testfiles/rendering_tests/selector-important-002.svg @@ -0,0 +1,58 @@ + + + Style "!important" — 002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/testfiles/rendering_tests/selector-important-003.svg b/testfiles/rendering_tests/selector-important-003.svg new file mode 100644 index 0000000..831319f --- /dev/null +++ b/testfiles/rendering_tests/selector-important-003.svg @@ -0,0 +1,57 @@ + + + Style "!important" — 003 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/testfiles/rendering_tests/style-parsing.svg b/testfiles/rendering_tests/style-parsing.svg new file mode 100644 index 0000000..33e32cb --- /dev/null +++ b/testfiles/rendering_tests/style-parsing.svg @@ -0,0 +1,16 @@ + + + + + + + + + diff --git a/testfiles/rendering_tests/symbol-svg2-geometry-properties.svg b/testfiles/rendering_tests/symbol-svg2-geometry-properties.svg new file mode 100644 index 0000000..a346366 --- /dev/null +++ b/testfiles/rendering_tests/symbol-svg2-geometry-properties.svg @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/testfiles/rendering_tests/symbol-svg2-ref.svg b/testfiles/rendering_tests/symbol-svg2-ref.svg new file mode 100644 index 0000000..25dfd3f --- /dev/null +++ b/testfiles/rendering_tests/symbol-svg2-ref.svg @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/testfiles/rendering_tests/test-baseline-shift.svg b/testfiles/rendering_tests/test-baseline-shift.svg new file mode 100644 index 0000000..7d20d22 --- /dev/null +++ b/testfiles/rendering_tests/test-baseline-shift.svg @@ -0,0 +1,33 @@ + + + + + + + subscript: H2O + superscript: m2 + subscript: H2O + superscript: m2 + + + Sub- and Superscript + + diff --git a/testfiles/rendering_tests/test-dont-crash.svg b/testfiles/rendering_tests/test-dont-crash.svg new file mode 100644 index 0000000..9eabd33 --- /dev/null +++ b/testfiles/rendering_tests/test-dont-crash.svg @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/testfiles/rendering_tests/test-empty.svg b/testfiles/rendering_tests/test-empty.svg new file mode 100644 index 0000000..3b5ee5a --- /dev/null +++ b/testfiles/rendering_tests/test-empty.svg @@ -0,0 +1,65 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/testfiles/rendering_tests/test-glyph-y-pos.svg b/testfiles/rendering_tests/test-glyph-y-pos.svg new file mode 100644 index 0000000..6f44ef8 --- /dev/null +++ b/testfiles/rendering_tests/test-glyph-y-pos.svg @@ -0,0 +1,32 @@ + + + + + + + + G̃g̃X̃x̃ + G̃g̃X̃x̃ + G̃g̃X̃x̃ + + + Composed Glyphs + + diff --git a/testfiles/rendering_tests/test-powerstroke-join.svg b/testfiles/rendering_tests/test-powerstroke-join.svg new file mode 100644 index 0000000..2c05fb3 --- /dev/null +++ b/testfiles/rendering_tests/test-powerstroke-join.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/testfiles/rendering_tests/test-rtl-vertical.svg b/testfiles/rendering_tests/test-rtl-vertical.svg new file mode 100644 index 0000000..aa6610b --- /dev/null +++ b/testfiles/rendering_tests/test-rtl-vertical.svg @@ -0,0 +1,39 @@ + + + + + + + + ۣۚۏۯ + ۣۚۏۯ + ۣۚۏۯ + + + RTL text in vertical mode + + diff --git a/testfiles/rendering_tests/test-use-ref.svg b/testfiles/rendering_tests/test-use-ref.svg new file mode 100644 index 0000000..0e6edf3 --- /dev/null +++ b/testfiles/rendering_tests/test-use-ref.svg @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/testfiles/rendering_tests/test-use.svg b/testfiles/rendering_tests/test-use.svg new file mode 100644 index 0000000..7e8a8ac --- /dev/null +++ b/testfiles/rendering_tests/test-use.svg @@ -0,0 +1,18 @@ + + + + + diff --git a/testfiles/rendering_tests/test.sh b/testfiles/rendering_tests/test.sh new file mode 100755 index 0000000..658b578 --- /dev/null +++ b/testfiles/rendering_tests/test.sh @@ -0,0 +1,66 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0-or-later + +MY_LOCATION=$(dirname "$0") +source "${MY_LOCATION}/../utils/functions.sh" + +ensure_command "compare" +ensure_command "bc" + +if [ "$#" -lt 2 ]; then + echo "Pass the path of the inkscape executable as parameter then the name of the test" $# + exit 1 +fi + +INKSCAPE_EXE="$1" +TEST="$2" +FUZZ="$3" +EXIT_STATUS=0 +EXPECTED="$(dirname "$TEST")/expected_rendering/$(basename "$TEST")" +TESTNAME="$(basename "$TEST")" + +if [ "$FUZZ" = "" ]; then + METRIC="AE" +else + METRIC="RMSE" +fi + +perform_test() +{ + local SUFFIX="$1" + local DPI="$2" + ${INKSCAPE_EXE} --export-png-use-dithering false --export-filename="${TESTNAME}${SUFFIX}.png" -d "$DPI" "${TEST}.svg" + + COMPARE_OUTPUT="$(compare -metric "$METRIC" "${TESTNAME}${SUFFIX}.png" "${EXPECTED}${SUFFIX}.png" "${TESTNAME}-compare${SUFFIX}.png" 2>&1)" + + if [ "$FUZZ" = "" ]; then + if [ "$COMPARE_OUTPUT" = 0 ]; then + echo "${TESTNAME}${SUFFIX}" "PASSED; absolute difference is exactly zero." + rm "${TESTNAME}${SUFFIX}.png" "${TESTNAME}-compare${SUFFIX}.png" + else + echo "${TESTNAME} FAILED; absolute difference ${COMPARE_OUTPUT} is greater than zero." + EXIT_STATUS=1 + fi + else + RELATIVE_ERROR=$(get_compare_result "$COMPARE_OUTPUT") + PERCENTAGE_ERROR=$(fraction_to_percentage "$RELATIVE_ERROR") + if (( $(is_relative_error_within_tolerance "$RELATIVE_ERROR" "$FUZZ") )) + then + echo "${TESTNAME}${SUFFIX}" "PASSED; error of ${PERCENTAGE_ERROR}% is within ${FUZZ}% tolerance." + rm "${TESTNAME}${SUFFIX}.png" "${TESTNAME}-compare${SUFFIX}.png" + else + echo "${TESTNAME} FAILED; error of ${PERCENTAGE_ERROR}% exceeds ${FUZZ}% tolerance." + EXIT_STATUS=1 + fi + fi +} + +perform_test "" 96 + +if [ -f "${EXPECTED}-large.png" ]; then + perform_test "-large" 384 +else + echo "${TESTNAME}-large" "SKIPPED" +fi + +exit $EXIT_STATUS diff --git a/testfiles/rendering_tests/text-flow.svg b/testfiles/rendering_tests/text-flow.svg new file mode 100644 index 0000000..9cade53 --- /dev/null +++ b/testfiles/rendering_tests/text-flow.svg @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.Why do we use it?It is a long established fact that a reader will be distracted by the readable.... + + Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.Why do we use it?It is a long established fact that a reader will be distracted by the readable.... + + Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.Why do we use it?It is a long established fact that a reader will be distracted by the readable.... + diff --git a/testfiles/rendering_tests/text-glyphs-combining.svg b/testfiles/rendering_tests/text-glyphs-combining.svg new file mode 100644 index 0000000..646ee05 --- /dev/null +++ b/testfiles/rendering_tests/text-glyphs-combining.svg @@ -0,0 +1,36 @@ + + + + + + + + õőo̓ơoÌŁoÌ«oÌłoÌ»o̓o͋o͗oÍĄ + õőo̓ơoÌŁoÌ«oÌłoÌ»o̓o͋o͗oÍĄ + õőo̓ơoÌŁoÌ«oÌłoÌ»o̓o͋o͗oÍĄ + õőo̓ơoÌŁoÌ«oÌłoÌ»o̓o͋o͗oÍĄ + + + + + + + + diff --git a/testfiles/rendering_tests/text-glyphs-vertical.svg b/testfiles/rendering_tests/text-glyphs-vertical.svg new file mode 100644 index 0000000..585a404 --- /dev/null +++ b/testfiles/rendering_tests/text-glyphs-vertical.svg @@ -0,0 +1,53 @@ + + + + + + + + ㆕㆖㆘Ać›žăƒŒà€„G̃g̃X̃x̃ + ㆕㆖㆘Ać›žăƒŒà€„G̃g̃X̃x̃ + ㆕㆖㆘Ać›žăƒŒà€„G̃g̃X̃x̃ + ㆕㆖㆘Ać›žăƒŒà€„G̃g̃X̃x̃ + + + + + + + + diff --git a/testfiles/rendering_tests/text-gzipped-svg-glyph.svg b/testfiles/rendering_tests/text-gzipped-svg-glyph.svg new file mode 100644 index 0000000..88ba336 --- /dev/null +++ b/testfiles/rendering_tests/text-gzipped-svg-glyph.svg @@ -0,0 +1,25 @@ + + + + + + ABC + + diff --git a/testfiles/rendering_tests/text-shaping.svg b/testfiles/rendering_tests/text-shaping.svg new file mode 100644 index 0000000..80aa95a --- /dev/null +++ b/testfiles/rendering_tests/text-shaping.svg @@ -0,0 +1,93 @@ + + + + + + + + + Ù†ÛŒŰ±Ùˆ + + ŰšÙŰłÙ’Ù…Ù Ű§Ù„Ù„Ù‘ÙŽÙ‡Ù Ű§Ù„Ű±Ù‘ÙŽŰ­Ù’Ù…ÙŽÙ†Ù Ű§Ù„Ű±Ù‘ÙŽŰ­ÙÙŠÙ…Ù + + Ś©ŚÖžŚœŚ•Ö覝 + + Ś—ÖŽŚ™ŚšÖŽŚ™Ś§ + + Ăą ĂąÌ‚ â â̂ + + a aÌ„ ą aÌš + + ヘ ペ ペ + + ఀెà°Čుగుà°Čో + + + గ్రంఄటà°Čà°Żà°‚ + + + ఇంక్‌ఞ్కేà°Ș్ + + diff --git a/testfiles/src/2geom-characterization-test.cpp b/testfiles/src/2geom-characterization-test.cpp new file mode 100644 index 0000000..d3f099b --- /dev/null +++ b/testfiles/src/2geom-characterization-test.cpp @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** @file + * 2Geom Lib characterization tests + *//* + * Authors: see git history + * + * Copyright (C) 2020 Authors + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ +#include + +#include <2geom/path.h> + +TEST(Characterization2Geom, retrievingBackElementOfAnEmptyClosedPathFails) +{ + Geom::Path path(Geom::Point(3, 5)); + path.close(); + ASSERT_TRUE(path.closed()); + ASSERT_EQ(path.size_closed(), 0u); +} + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/testfiles/src/async_channel-test.cpp b/testfiles/src/async_channel-test.cpp new file mode 100644 index 0000000..12dfc6f --- /dev/null +++ b/testfiles/src/async_channel-test.cpp @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include +#include +#include +#include +#include "async/channel.h" +using namespace Inkscape::Async; + +TEST(Channel, channel) +{ + auto test_one = [] (bool soft_close, bool delay_src_destroy, bool delay_dst_destroy) { + auto mainloop = Glib::MainLoop::create(); + + std::optional src; + std::optional dst; + std::tie(src, dst) = Channel::create(); + + std::thread thread; + std::vector results; + + Glib::signal_idle().connect([&] { + thread = std::thread([&] { + EXPECT_TRUE(src); + + EXPECT_TRUE(src->run([&] { results.emplace_back(1); })); // insert temporary function + + auto f = [&, x = 2] { results.emplace_back(x); }; + EXPECT_TRUE(src->run(f)); // insert copy of function + + auto g = [&, x = 3] { results.emplace_back(x); }; + EXPECT_TRUE(src->run(std::move(g))); // insert function by move + + // insert function which closes channel + EXPECT_TRUE(src->run([&] { + ASSERT_TRUE(dst); + ASSERT_TRUE(*dst); + + if (delay_dst_destroy) { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } + + if (soft_close) { + dst->close(); + EXPECT_FALSE(*dst); + } else { + dst.reset(); + } + + mainloop->quit(); + })); + + src->run([&] { results.emplace_back(4); }); + + if (delay_src_destroy) { + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + src->run([&] { results.emplace_back(5); }); + } + + src.reset(); + }); + + return false; + }); + + mainloop->run(); + thread.join(); + + EXPECT_EQ(results, (std::vector{ 1, 2, 3 })); + }; + + for (bool x : { true, false }) { + test_one(x, false, false); + test_one(x, true, false); + test_one(x, false, true); + } +} diff --git a/testfiles/src/async_funclog-test.cpp b/testfiles/src/async_funclog-test.cpp new file mode 100644 index 0000000..a7dac7a --- /dev/null +++ b/testfiles/src/async_funclog-test.cpp @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include +#include +#include "util/funclog.h" +using namespace Inkscape::Util; + +static int counter; + +class LoggedInt +{ +public: + LoggedInt(int x) : x(x) { counter++; } + LoggedInt(LoggedInt const &other) noexcept : x(other.x) { counter++; } + LoggedInt &operator=(LoggedInt const &other) noexcept { x = other.x; return *this; } + ~LoggedInt() { counter--; } + operator int() const { return x; } + LoggedInt &operator=(int x2) { x = x2; return *this; } + +private: + int x; +}; + +TEST(FuncLogTest, funclog) +{ + counter = 0; + + std::vector results; + auto write = [&] (int x) { return [&, x = LoggedInt(x)] { results.emplace_back(x); }; }; + + auto compare = [&] (std::vector expected) { + EXPECT_EQ(results, expected); + results.clear(); + }; + + FuncLog a; + EXPECT_TRUE(a.empty()); + a(); + compare({}); + + a.emplace(write(1)); + a.emplace(write(2)); + EXPECT_EQ(counter, 2); + EXPECT_FALSE(a.empty()); + a(); + compare({ 1, 2 }); + + a.emplace(write(3)); + auto b = std::move(a); + a(); + compare({}); + b(); + compare({ 3 }); + auto c = std::move(a); + c(); + compare({}); + + b.emplace(write(4)); + a = std::move(b); + b(); + compare({}); + a(); + compare({ 4 }); + a(); + compare({}); + + for (int N : { 10, 50, 10, 100, 10, 500, 10 }) { + for (int i = 0; i < N; i++) { + a.emplace(write(4)); + a.emplace([&, x = i, y = 2 * i, z = 3 * i, w = 4 * i] { results.emplace_back(x + y + z + w); }); + } + + a(); + + ASSERT_EQ(results.size(), 2 * N); + for (int i = 0; i < N; i++) { + ASSERT_EQ(results[2 * i], 4); + ASSERT_EQ(results[2 * i + 1], 10 * i); + } + results.clear(); + } + + { + auto f1 = [&, x = 1] { + results.emplace_back(x); + }; + a.emplace(f1); + + auto f2 = [&, x = 2] { + results.emplace_back(x); + }; + a.emplace(std::move(f2)); + } + + a(); + compare({ 1, 2 }); + + a.emplace([&, x = std::make_unique(5)] { results.emplace_back(*x); }); + a(); + compare({ 5 }); + + FuncLog().emplace(write(6)); + compare({}); + + for (int i = 0; i < 5; i++) { + a.emplace(write(i)); + } + a.exec_while([counter = 0] () mutable { return ++counter <= 3; }); + compare({ 0, 1, 2 }); + EXPECT_TRUE(a.empty()); + + struct ExceptionMock {}; + for (int i = 0; i < 5; i++) { + a.emplace([&, i] { if (i == 3) throw ExceptionMock(); results.emplace_back(i); }); + } + EXPECT_THROW(a(), ExceptionMock); + compare({ 0, 1, 2 }); + EXPECT_TRUE(a.empty()); + + ASSERT_EQ(counter, 0); +} diff --git a/testfiles/src/async_progress-test.cpp b/testfiles/src/async_progress-test.cpp new file mode 100644 index 0000000..039d6ce --- /dev/null +++ b/testfiles/src/async_progress-test.cpp @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include +#include +#include +#include +#include "async/progress.h" +#include "async/progress-splitter.h" +using namespace Inkscape::Async; + +TEST(ProgressTest, subprogress) +{ + class ProgressMock final + : public Progress + { + public: + mutable bool k_called; + double p_arg_saved; + bool ret; + + void reset(bool ret_) + { + k_called = false; + p_arg_saved = -1.0; + ret = ret_; + } + + protected: + bool _keepgoing() const override { k_called = true; return ret; } + bool _report(double const &progress) override { p_arg_saved = progress; return ret; } + }; + + auto a = ProgressMock(); + auto b = SubProgress(a, 0.25, 0.5); + auto c = SubProgress(b, 0.1, 0.2); + + for (bool ret : { true, false }) { + for (double progress = 0.0; progress < 1.0; progress += 0.3) { + a.reset(ret); + EXPECT_EQ(c.report(progress), ret); + EXPECT_NEAR(a.p_arg_saved, 0.25 + 0.5 * (0.1 + 0.2 * progress), 1e-5); + EXPECT_FALSE(a.k_called); + } + } + + for (bool ret : { true, false }) { + a.reset(ret); + EXPECT_EQ(c.keepgoing(), ret); + EXPECT_EQ(a.p_arg_saved, -1.0); + EXPECT_TRUE(a.k_called); + } + + a.reset(false); + EXPECT_THROW(c.report_or_throw(0.5), CancelledException); + EXPECT_THROW(c.throw_if_cancelled(), CancelledException); + a.reset(true); + EXPECT_NO_THROW(c.report_or_throw(0.5)); + EXPECT_NO_THROW(c.throw_if_cancelled()); +} + +TEST(ProgressTest, throttler) +{ + class ProgressMock final + : public Progress + { + public: + int calls = 0; + double saved = 0.0; + + protected: + bool _keepgoing() const override { return true; } + bool _report(double const &progress) override { saved = progress; calls++; return true; } + }; + + double constexpr step = 0.1; + auto a = ProgressMock(); + auto b = ProgressStepThrottler(a, step); + + int constexpr N = 1000; + for (int i = 0; i < N; i++) { + double progress = (double)i / N; + b.report(progress); + ASSERT_LE(std::abs(progress - a.saved), 1.1 * step); + } + ASSERT_GE(a.calls, 9); + ASSERT_LE(a.calls, 11); +} + +TEST(ProgressTest, splitter) +{ + class ProgressMock final + : public Progress + { + public: + double saved; + + protected: + bool _keepgoing() const override { return true; } + bool _report(double const &progress) override { saved = progress; return true; } + }; + + auto a = ProgressMock(); + std::optional> x, y, z; + + auto reset = [&] { + a.saved = -1.0; + x = y = z = {}; + }; + + reset(); + ProgressSplitter(a) + .add(x, 0.25) + .add(y, 0.5) + .add(z, 0.25); + ASSERT_TRUE(x); + ASSERT_TRUE(y); + ASSERT_TRUE(z); + x->report(0.5); EXPECT_NEAR(a.saved, 0.125, 1e-5); + y->report(0.5); EXPECT_NEAR(a.saved, 0.5 , 1e-5); + z->report(0.5); EXPECT_NEAR(a.saved, 0.875, 1e-5); + + reset(); + ProgressSplitter(a) + .add_if(x, 0.25, true) + .add_if(y, 0.5, false) + .add_if(z, 0.25, true); + ASSERT_TRUE(x); + ASSERT_FALSE(y); + ASSERT_TRUE(z); + x->report(0.5); EXPECT_NEAR(a.saved, 0.25, 1e-5); + z->report(0.5); EXPECT_NEAR(a.saved, 0.75, 1e-5); +} diff --git a/testfiles/src/attributes-test.cpp b/testfiles/src/attributes-test.cpp new file mode 100644 index 0000000..2fa8059 --- /dev/null +++ b/testfiles/src/attributes-test.cpp @@ -0,0 +1,685 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/* + * Unit tests for attributes. + * + * Author: + * Jon A. Cruz + * + * Copyright (C) 2015 Authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include +#include +#include + +#include "gtest/gtest.h" + +#include "attributes.h" + +namespace { + +static const unsigned int FIRST_VALID_ID = 1; + +class AttributeInfo +{ +public: + AttributeInfo(std::string attr, bool supported) : + attr(std::move(attr)), + supported(supported) + { + } + + std::string attr; + bool supported; +}; + +typedef std::vector::iterator AttrItr; + +std::vector getKnownAttrs() +{ +/* Originally extracted mechanically from + http://www.w3.org/TR/SVG11/attindex.html: + + tidy -wrap 999 -asxml < attindex.html 2>/dev/null | + tr -d \\n | + sed 's,,@,g' | + tr @ \\n | + sed 's,.*,,;s,^,,;1,/^%/d;/^%/d;s,^, {",;s/$/", false},/' | + uniq + + attindex.html lacks attributeName, begin, additive, font, marker; + I've added these manually. + + SVG 2: white-space, shape-inside, shape-subtrace, shape-padding, shape-margin +*/ + AttributeInfo all_attrs[] = { + AttributeInfo("attributeName", true), + AttributeInfo("begin", true), + AttributeInfo("additive", true), + AttributeInfo("font", true), + AttributeInfo("-inkscape-font-specification", true), // TODO look into this attribute's name + AttributeInfo("marker", true), + AttributeInfo("line-height", true), + + AttributeInfo("accent-height", true), + AttributeInfo("accumulate", true), + AttributeInfo("alignment-baseline", true), + AttributeInfo("alphabetic", true), + AttributeInfo("amplitude", true), + AttributeInfo("animate", false), + AttributeInfo("arabic-form", true), + AttributeInfo("ascent", true), + AttributeInfo("attributeType", true), + AttributeInfo("azimuth", true), + AttributeInfo("baseFrequency", true), + AttributeInfo("baseline-shift", true), + AttributeInfo("baseProfile", false), + AttributeInfo("bbox", true), + AttributeInfo("bias", true), + AttributeInfo("by", true), + AttributeInfo("calcMode", true), + AttributeInfo("cap-height", true), + AttributeInfo("class", false), + AttributeInfo("clip", true), + AttributeInfo("clip-path", true), + AttributeInfo("clip-rule", true), + AttributeInfo("clipPathUnits", true), + AttributeInfo("color", true), + AttributeInfo("color-interpolation", true), + AttributeInfo("color-interpolation-filters", true), + AttributeInfo("color-profile", true), + AttributeInfo("color-rendering", true), + AttributeInfo("contentScriptType", false), + AttributeInfo("contentStyleType", false), + AttributeInfo("cursor", true), + AttributeInfo("cx", true), + AttributeInfo("cy", true), + AttributeInfo("d", true), + AttributeInfo("descent", true), + AttributeInfo("diffuseConstant", true), + AttributeInfo("direction", true), + AttributeInfo("display", true), + AttributeInfo("divisor", true), + AttributeInfo("dominant-baseline", true), + AttributeInfo("dur", true), + AttributeInfo("dx", true), + AttributeInfo("dy", true), + AttributeInfo("edgeMode", true), + AttributeInfo("elevation", true), + AttributeInfo("enable-background", true), + AttributeInfo("end", true), + AttributeInfo("exponent", true), + AttributeInfo("externalResourcesRequired", false), + AttributeInfo("feBlend", false), + AttributeInfo("feColorMatrix", false), + AttributeInfo("feComponentTransfer", false), + AttributeInfo("feComposite", false), + AttributeInfo("feConvolveMatrix", false), + AttributeInfo("feDiffuseLighting", false), + AttributeInfo("feDisplacementMap", false), + AttributeInfo("feFlood", false), + AttributeInfo("feGaussianBlur", false), + AttributeInfo("feImage", false), + AttributeInfo("feMerge", false), + AttributeInfo("feMorphology", false), + AttributeInfo("feOffset", false), + AttributeInfo("feSpecularLighting", false), + AttributeInfo("feTile", false), + AttributeInfo("fill", true), + AttributeInfo("fill-opacity", true), + AttributeInfo("fill-rule", true), + AttributeInfo("filter", true), + AttributeInfo("filterRes", true), + AttributeInfo("filterUnits", true), + AttributeInfo("flood-color", true), + AttributeInfo("flood-opacity", true), + AttributeInfo("font-family", true), + AttributeInfo("font-feature-settings", true), + AttributeInfo("font-size", true), + AttributeInfo("font-size-adjust", true), + AttributeInfo("font-stretch", true), + AttributeInfo("font-style", true), + AttributeInfo("font-variant", true), + AttributeInfo("font-variant-ligatures", true), + AttributeInfo("font-variant-position", true), + AttributeInfo("font-variant-caps", true), + AttributeInfo("font-variant-numeric", true), + AttributeInfo("font-variant-east-asian", true), + AttributeInfo("font-variant-alternates", true), + AttributeInfo("font-variation-settings", true), + AttributeInfo("font-weight", true), + AttributeInfo("format", false), + AttributeInfo("from", true), + AttributeInfo("fx", true), + AttributeInfo("fr", true), + AttributeInfo("fy", true), + AttributeInfo("g1", true), + AttributeInfo("g2", true), + AttributeInfo("glyph-name", true), + AttributeInfo("glyph-orientation-horizontal", true), + AttributeInfo("glyph-orientation-vertical", true), + AttributeInfo("glyphRef", false), + AttributeInfo("gradientTransform", true), + AttributeInfo("gradientUnits", true), + AttributeInfo("hanging", true), + AttributeInfo("hatchContentUnits", true), // SVG 2.0 + AttributeInfo("hatchTransform", true), // SVG 2.0 TODO renamed to transform + AttributeInfo("hatchUnits", true), // SVG 2.0 + AttributeInfo("height", true), + AttributeInfo("horiz-adv-x", true), + AttributeInfo("horiz-origin-x", true), + AttributeInfo("horiz-origin-y", true), + AttributeInfo("ideographic", true), + AttributeInfo("image-rendering", true), + AttributeInfo("in", true), + AttributeInfo("in2", true), + AttributeInfo("inline-size", true), + AttributeInfo("intercept", true), + AttributeInfo("isolation", true), + AttributeInfo("k", true), + AttributeInfo("k1", true), + AttributeInfo("k2", true), + AttributeInfo("k3", true), + AttributeInfo("k4", true), + AttributeInfo("kernelMatrix", true), + AttributeInfo("kernelUnitLength", true), + AttributeInfo("kerning", true), + AttributeInfo("keyPoints", false), + AttributeInfo("keySplines", true), + AttributeInfo("keyTimes", true), + AttributeInfo("lang", true), + AttributeInfo("lengthAdjust", true), + AttributeInfo("letter-spacing", true), + AttributeInfo("lighting-color", true), + AttributeInfo("inkscape:auto-region", true), + AttributeInfo("limitingConeAngle", true), + AttributeInfo("local", true), + AttributeInfo("marker-end", true), + AttributeInfo("marker-mid", true), + AttributeInfo("marker-start", true), + AttributeInfo("markerHeight", true), + AttributeInfo("markerUnits", true), + AttributeInfo("markerWidth", true), + AttributeInfo("mask", true), + AttributeInfo("maskContentUnits", true), + AttributeInfo("maskUnits", true), + AttributeInfo("mathematical", true), + AttributeInfo("max", true), + AttributeInfo("media", false), + AttributeInfo("method", false), + AttributeInfo("min", true), + AttributeInfo("mix-blend-mode", true), + AttributeInfo("mode", true), + AttributeInfo("name", true), + AttributeInfo("numOctaves", true), + AttributeInfo("offset", true), + AttributeInfo("onabort", false), + AttributeInfo("onactivate", false), + AttributeInfo("onbegin", false), + AttributeInfo("onclick", false), + AttributeInfo("onend", false), + AttributeInfo("onerror", false), + AttributeInfo("onfocusin", false), + AttributeInfo("onfocusout", false), + AttributeInfo("onload", true), + AttributeInfo("onmousedown", false), + AttributeInfo("onmousemove", false), + AttributeInfo("onmouseout", false), + AttributeInfo("onmouseover", false), + AttributeInfo("onmouseup", false), + AttributeInfo("onrepeat", false), + AttributeInfo("onresize", false), + AttributeInfo("onscroll", false), + AttributeInfo("onunload", false), + AttributeInfo("onzoom", false), + AttributeInfo("opacity", true), + AttributeInfo("operator", true), + AttributeInfo("order", true), + AttributeInfo("orient", true), + AttributeInfo("orientation", true), + AttributeInfo("origin", false), + AttributeInfo("overflow", true), + AttributeInfo("overline-position", true), + AttributeInfo("overline-thickness", true), + AttributeInfo("paint-order", true), + AttributeInfo("panose-1", true), + AttributeInfo("path", true), + AttributeInfo("pathLength", false), + AttributeInfo("patternContentUnits", true), + AttributeInfo("patternTransform", true), + AttributeInfo("patternUnits", true), + AttributeInfo("pitch", true), // SVG 2.- + AttributeInfo("pointer-events", true), + AttributeInfo("points", true), + AttributeInfo("pointsAtX", true), + AttributeInfo("pointsAtY", true), + AttributeInfo("pointsAtZ", true), + AttributeInfo("preserveAlpha", true), + AttributeInfo("preserveAspectRatio", true), + AttributeInfo("primitiveUnits", true), + AttributeInfo("r", true), + AttributeInfo("radius", true), + AttributeInfo("refX", true), + AttributeInfo("refY", true), + AttributeInfo("rendering-intent", true), + AttributeInfo("repeatCount", true), + AttributeInfo("repeatDur", true), + AttributeInfo("requiredFeatures", true), + AttributeInfo("requiredExtensions", true), + AttributeInfo("restart", true), + AttributeInfo("result", true), + AttributeInfo("rotate", true), + AttributeInfo("rx", true), + AttributeInfo("ry", true), + AttributeInfo("scale", true), + AttributeInfo("seed", true), + AttributeInfo("shape-inside", true), + AttributeInfo("shape-margin", true), + AttributeInfo("shape-subtract", true), + AttributeInfo("shape-padding", true), + AttributeInfo("shape-rendering", true), + AttributeInfo("side", true), + AttributeInfo("slope", true), + AttributeInfo("solid-color", true), // SVG 2.0 + AttributeInfo("solid-opacity", true), // SVG 2.0 + AttributeInfo("spacing", false), + AttributeInfo("specularConstant", true), + AttributeInfo("specularExponent", true), + AttributeInfo("spreadMethod", true), + AttributeInfo("startOffset", true), + AttributeInfo("stdDeviation", true), + AttributeInfo("stemh", true), + AttributeInfo("stemv", true), + AttributeInfo("stitchTiles", true), + AttributeInfo("stop-color", true), + AttributeInfo("stop-opacity", true), + AttributeInfo("strikethrough-position", true), + AttributeInfo("strikethrough-thickness", true), + AttributeInfo("stroke", true), + AttributeInfo("stroke-dasharray", true), + AttributeInfo("stroke-dashoffset", true), + AttributeInfo("stroke-linecap", true), + AttributeInfo("stroke-linejoin", true), + AttributeInfo("stroke-miterlimit", true), + AttributeInfo("stroke-opacity", true), + AttributeInfo("stroke-width", true), + AttributeInfo("style", true), + AttributeInfo("surfaceScale", true), + AttributeInfo("systemLanguage", true), + AttributeInfo("tableValues", true), + AttributeInfo("target", true), + AttributeInfo("targetX", true), + AttributeInfo("targetY", true), + AttributeInfo("text-align", true), + AttributeInfo("text-anchor", true), + AttributeInfo("text-decoration", true), + AttributeInfo("text-decoration-color", true), + AttributeInfo("text-decoration-fill", true), + AttributeInfo("text-decoration-line", true), + AttributeInfo("text-decoration-stroke", true), + AttributeInfo("text-decoration-style", true), + AttributeInfo("text-indent", true), + AttributeInfo("text-orientation", true), + AttributeInfo("text-rendering", true), + AttributeInfo("text-transform", true), + AttributeInfo("textLength", true), + AttributeInfo("title", false), + AttributeInfo("to", true), + AttributeInfo("transform", true), + AttributeInfo("type", true), + AttributeInfo("u1", true), + AttributeInfo("u2", true), + AttributeInfo("underline-position", true), + AttributeInfo("underline-thickness", true), + AttributeInfo("unicode", true), + AttributeInfo("unicode-bidi", true), + AttributeInfo("unicode-range", true), + AttributeInfo("units-per-em", true), + AttributeInfo("v-alphabetic", true), + AttributeInfo("v-hanging", true), + AttributeInfo("v-ideographic", true), + AttributeInfo("v-mathematical", true), + AttributeInfo("values", true), + AttributeInfo("vector-effect", true), + AttributeInfo("version", true), + AttributeInfo("vert-adv-y", true), + AttributeInfo("vert-origin-x", true), + AttributeInfo("vert-origin-y", true), + AttributeInfo("viewBox", true), + AttributeInfo("viewTarget", false), + AttributeInfo("visibility", true), + AttributeInfo("white-space", true), + AttributeInfo("width", true), + AttributeInfo("widths", true), + AttributeInfo("word-spacing", true), + AttributeInfo("writing-mode", true), + AttributeInfo("x", true), + AttributeInfo("x-height", true), + AttributeInfo("x1", true), + AttributeInfo("x2", true), + AttributeInfo("xChannelSelector", true), + AttributeInfo("xlink:actuate", true), + AttributeInfo("xlink:arcrole", true), + AttributeInfo("xlink:href", true), + AttributeInfo("xlink:role", true), + AttributeInfo("xlink:show", true), + AttributeInfo("xlink:title", true), + AttributeInfo("xlink:type", true), + AttributeInfo("xml:base", false), + AttributeInfo("xml:lang", true), + AttributeInfo("xml:space", true), + AttributeInfo("xmlns", false), + AttributeInfo("xmlns:xlink", false), + AttributeInfo("y", true), + AttributeInfo("y1", true), + AttributeInfo("y2", true), + AttributeInfo("yChannelSelector", true), + AttributeInfo("z", true), + AttributeInfo("zoomAndPan", false), + + // Extra attributes. + AttributeInfo("-inkscape-stroke", true), + AttributeInfo("id", true), + // AttributeInfo("inkscape:bbox-nodes", true), + // AttributeInfo("inkscape:bbox-paths", true), + AttributeInfo("inkscape:deskcolor", true), + AttributeInfo("inkscape:deskopacity", true), + AttributeInfo("inkscape:box3dsidetype", true), + AttributeInfo("inkscape:collect", true), + AttributeInfo("inkscape:color", true), + AttributeInfo("inkscape:connection-end", true), + AttributeInfo("inkscape:connection-end-point", true), + AttributeInfo("inkscape:connection-points", true), + AttributeInfo("inkscape:connection-start", true), + AttributeInfo("inkscape:connection-start-point", true), + AttributeInfo("inkscape:connector-avoid", true), + AttributeInfo("inkscape:connector-curvature", true), + AttributeInfo("inkscape:connector-spacing", true), + AttributeInfo("inkscape:connector-type", true), + AttributeInfo("inkscape:corner0", true), + AttributeInfo("inkscape:corner7", true), + AttributeInfo("inkscape:current-layer", true), + AttributeInfo("inkscape:cx", true), + AttributeInfo("inkscape:cy", true), + AttributeInfo("inkscape:rotation", true), + AttributeInfo("inkscape:document-units", true), + AttributeInfo("inkscape:dstBox", true), + AttributeInfo("inkscape:dstColumn", true), + AttributeInfo("inkscape:dstPath", true), + AttributeInfo("inkscape:dstShape", true), + AttributeInfo("inkscape:excludeShape", true), + AttributeInfo("inkscape:expanded", true), + AttributeInfo("inkscape:flatsided", true), + AttributeInfo("inkscape:groupmode", true), + AttributeInfo("inkscape:highlight-color", true), + AttributeInfo("inkscape:href", true), + AttributeInfo("inkscape:label", true), + AttributeInfo("inkscape:layoutOptions", true), + AttributeInfo("inkscape:lockguides", true), + AttributeInfo("inkscape:locked", true), + AttributeInfo("margin", true), + AttributeInfo("bleed", true), + AttributeInfo("page-size", true), + // AttributeInfo("inkscape:object-nodes", true), + // AttributeInfo("inkscape:object-paths", true), + AttributeInfo("inkscape:original", true), + AttributeInfo("inkscape:original-d", true), + AttributeInfo("inkscape:pagecheckerboard", true), + AttributeInfo("inkscape:pageopacity", true), + AttributeInfo("inkscape:pageshadow", true), + AttributeInfo("inkscape:path-effect", true), + AttributeInfo("inkscape:persp3d", true), + AttributeInfo("inkscape:persp3d-origin", true), + AttributeInfo("inkscape:perspectiveID", true), + AttributeInfo("inkscape:radius", true), + AttributeInfo("inkscape:randomized", true), + AttributeInfo("inkscape:rounded", true), + // AttributeInfo("inkscape:snap-alignment", true), + // AttributeInfo("inkscape:snap-alignment-self", true), + // AttributeInfo("inkscape:snap-distribution", true), + // AttributeInfo("inkscape:snap-bbox", true), + // AttributeInfo("inkscape:snap-bbox-edge-midpoints", true), + // AttributeInfo("inkscape:snap-bbox-midpoints", true), + // AttributeInfo("inkscape:snap-center", true), + // AttributeInfo("inkscape:snap-global", true), + // AttributeInfo("inkscape:snap-grids", true), + // AttributeInfo("inkscape:snap-intersection-paths", true), + // AttributeInfo("inkscape:snap-midpoints", true), + // AttributeInfo("inkscape:snap-nodes", true), + // AttributeInfo("inkscape:snap-object-midpoints", true), + // AttributeInfo("inkscape:snap-others", true), + // AttributeInfo("inkscape:snap-from-guide", true), + // AttributeInfo("inkscape:snap-page", true), + // AttributeInfo("inkscape:snap-path-clip", true), + // AttributeInfo("inkscape:snap-path-mask", true), + // AttributeInfo("inkscape:snap-perpendicular", true), + // AttributeInfo("inkscape:snap-smooth-nodes", true), + // AttributeInfo("inkscape:snap-tangential", true), + // AttributeInfo("inkscape:snap-text-baseline", true), + // AttributeInfo("inkscape:snap-to-guides", true), + AttributeInfo("inkscape:spray-origin", true), + AttributeInfo("inkscape:srcNoMarkup", true), + AttributeInfo("inkscape:srcPango", true), + AttributeInfo("inkscape:transform-center-x", true), + AttributeInfo("inkscape:transform-center-y", true), + AttributeInfo("inkscape:version", true), + AttributeInfo("inkscape:vp_x", true), + AttributeInfo("inkscape:vp_y", true), + AttributeInfo("inkscape:vp_z", true), + AttributeInfo("inkscape:window-height", true), + AttributeInfo("inkscape:window-maximized", true), + AttributeInfo("inkscape:window-width", true), + AttributeInfo("inkscape:window-x", true), + AttributeInfo("inkscape:window-y", true), + AttributeInfo("inkscape:zoom", true), + AttributeInfo("inkscape:svg-dpi", true), + AttributeInfo("inkscape:swatch", true), + AttributeInfo("inkscape:pinned", true), + AttributeInfo("sodipodi:arc-type", true), + AttributeInfo("sodipodi:arg1", true), + AttributeInfo("sodipodi:arg2", true), + AttributeInfo("sodipodi:argument", true), + AttributeInfo("sodipodi:cx", true), + AttributeInfo("sodipodi:cy", true), + AttributeInfo("sodipodi:docname", true), + AttributeInfo("sodipodi:end", true), + AttributeInfo("sodipodi:expansion", true), + AttributeInfo("sodipodi:insensitive", true), + AttributeInfo("sodipodi:linespacing", true), + AttributeInfo("sodipodi:open", true), + AttributeInfo("sodipodi:original", true), + AttributeInfo("sodipodi:r1", true), + AttributeInfo("sodipodi:r2", true), + AttributeInfo("sodipodi:radius", true), + AttributeInfo("sodipodi:revolution", true), + AttributeInfo("sodipodi:role", true), + AttributeInfo("sodipodi:rx", true), + AttributeInfo("sodipodi:ry", true), + AttributeInfo("sodipodi:sides", true), + AttributeInfo("sodipodi:start", true), + AttributeInfo("sodipodi:t0", true), + AttributeInfo("sodipodi:type", true), + AttributeInfo("sodipodi:version", false), + + // SPMeshPatch + AttributeInfo("tensor", true), + + // SPNamedView + AttributeInfo("fit-margin-top", true), + AttributeInfo("fit-margin-left", true), + AttributeInfo("fit-margin-right", true), + AttributeInfo("fit-margin-bottom", true), + AttributeInfo("units", true), + AttributeInfo("viewonly", true), + AttributeInfo("showgrid", true), +// AttributeInfo("gridtype", true), + AttributeInfo("showguides", true), + AttributeInfo("gridtolerance", true), + AttributeInfo("guidetolerance", true), + AttributeInfo("objecttolerance", true), + AttributeInfo("alignmenttolerance", true), + AttributeInfo("distributiontolerance", true), +/* AttributeInfo("gridoriginx", true), + AttributeInfo("gridoriginy", true), + AttributeInfo("gridspacingx", true), + AttributeInfo("gridspacingy", true), + AttributeInfo("gridanglex", true), + AttributeInfo("gridanglez", true), + AttributeInfo("gridcolor", true), + AttributeInfo("gridopacity", true), + AttributeInfo("gridempcolor", true), + AttributeInfo("gridempopacity", true), + AttributeInfo("gridempspacing", true), */ + AttributeInfo("guidecolor", true), + AttributeInfo("guideopacity", true), + AttributeInfo("guidehicolor", true), + AttributeInfo("guidehiopacity", true), + AttributeInfo("showborder", true), + AttributeInfo("inkscape:showpageshadow", true), + AttributeInfo("borderlayer", true), + AttributeInfo("bordercolor", true), + AttributeInfo("borderopacity", true), + AttributeInfo("pagecolor", true), + AttributeInfo("labelstyle", true), + + // SPGrid + AttributeInfo("originx", true), + AttributeInfo("originy", true), + AttributeInfo("spacingx", true), + AttributeInfo("spacingy", true), + AttributeInfo("gridanglex", true), + AttributeInfo("gridanglez", true), + AttributeInfo("enabled", true), + AttributeInfo("visible", true), + AttributeInfo("empopacity", true), + AttributeInfo("empcolor", true), + AttributeInfo("empspacing", true), + AttributeInfo("dotted", true), + AttributeInfo("snapvisiblegridlinesonly", true), + + // SPGuide + AttributeInfo("position", true), + + // don't know what that is + AttributeInfo("effect", true) + }; + + size_t count = sizeof(all_attrs) / sizeof(all_attrs[0]); + std::vector vect(all_attrs, all_attrs + count); + EXPECT_GT(vect.size(), size_t(100)); // should be more than + return vect; +} + +/** + * Returns a vector with counts for all IDs up to the highest known value. + * + * The index is the ID, and the value is the number of times that ID is seen. + */ +std::vector getIdIds() +{ + std::vector ids; + std::vector all_attrs = getKnownAttrs(); + ids.reserve(all_attrs.size()); // minimize memory thrashing + for (auto & all_attr : all_attrs) { + auto id = sp_attribute_lookup(all_attr.attr.c_str()); + if ((int)id >= ids.size()) { + ids.resize((int)id + 1); + } + ids[(int)id]++; + } + + return ids; +} + +// Ensure 'supported' value for each known attribute is correct. +TEST(AttributesTest, SupportedKnown) +{ + std::vector all_attrs = getKnownAttrs(); + for (AttrItr it(all_attrs.begin()); it != all_attrs.end(); ++it) { + auto id = sp_attribute_lookup(it->attr.c_str()); + EXPECT_EQ(it->supported, id != SPAttr::INVALID) << "Matching for attribute '" << it->attr << "'"; + } +} + +// Ensure names of known attributes are preserved when converted to id and back. +TEST(AttributesTest, NameRoundTrip) +{ + std::vector all_attrs = getKnownAttrs(); + for (AttrItr it(all_attrs.begin()); it != all_attrs.end(); ++it) { + if (it->supported) { + auto id = sp_attribute_lookup(it->attr.c_str()); + char const *redoneName = sp_attribute_name(id); + EXPECT_TRUE(redoneName != NULL) << "For attribute '" << it->attr << "'"; + if (redoneName) { + EXPECT_EQ(it->attr, redoneName); + } + } + } +} + +// Equivalent aliases, e.g. with and without namespace +TEST(AttributesTest, Aliases) +{ + EXPECT_EQ(sp_attribute_lookup("href"), SPAttr::XLINK_HREF); +} + +/* Test for any attributes that this test program doesn't know about. + * + * If any are found, then: + * + * If it is in the `inkscape:' namespace then simply add it to all_attrs with + * `true' as the second field (`supported'). + * + * If it is in the `sodipodi:' namespace then check the spelling against sodipodi + * sources. If you don't have sodipodi sources, then don't add it: leave to someone + * else. + * + * Otherwise, it's probably a bug: ~all SVG 1.1 attributes should already be + * in the all_attrs table. However, the comment above all_attrs does mention + * some things missing from attindex.html, so there may be more. Check the SVG + * spec. Another possibility is that the attribute is new in SVG 1.2. In this case, + * check the spelling against the [draft] SVG 1.2 spec before adding to all_attrs. + * (If you can't be bothered checking the spec, then don't update all_attrs.) + * + * If the attribute isn't in either SVG 1.1 or 1.2 then it's probably a mistake + * for it not to be in the inkscape namespace. (Not sure about attributes used only + * on elements in the inkscape namespace though.) + * + * In any case, make sure that the attribute's source is documented accordingly. + */ +TEST(AttributesTest, ValuesAreKnown) +{ + std::vector ids = getIdIds(); + for (size_t i = FIRST_VALID_ID; i < ids.size(); ++i) { + if (!ids[i]) { + char const *name = sp_attribute_name((SPAttr)i); + EXPECT_TRUE(ids[i] > 0) << "Attribute string with enum " << i << " {" << name << "} not handled"; + } + } +} + +// Ensure two different names aren't mapped to the same enum value. +TEST(AttributesTest, ValuesUnique) +{ + std::vector ids = getIdIds(); + for (size_t i = FIRST_VALID_ID; i < ids.size(); ++i) { + EXPECT_LE(ids[i], size_t(1)) << "Attribute enum " << i << " used for multiple strings" + << " including {" << sp_attribute_name((SPAttr)i) << "}"; + } +} + +} // namespace + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/testfiles/src/cairo-utils-test.cpp b/testfiles/src/cairo-utils-test.cpp new file mode 100644 index 0000000..3412c4a --- /dev/null +++ b/testfiles/src/cairo-utils-test.cpp @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** @file + * Tests for classes like Pixbuf from cairo-utils + *//* + * Authors: see git history + * + * Copyright (C) 2020 Authors + * + * Released under GNU GPL version 2 or later, read the file 'COPYING' for more information + */ + +#include +#include +#include + + +class PixbufTest : public ::testing::Test { + public: + static std::string base64of(const std::string &s) + { + gchar *encoded = g_base64_encode(reinterpret_cast(s.c_str()), s.size()); + std::string r(encoded); + g_free(encoded); + return r; + } + + protected: + void SetUp() override + { + // setup hidden dependency + Inkscape::Application::create(false); + } +}; + +TEST_F(PixbufTest, creatingFromSvgBufferWithoutViewboxOrWidthAndHeightReturnsNull) +{ + std::string svg_buffer( + ""); + double default_dpi = 96.0; + std::string filename_with_svg_extension("malformed.svg"); + + ASSERT_EQ(Inkscape::Pixbuf::create_from_buffer(svg_buffer, default_dpi, filename_with_svg_extension), nullptr); +} + +TEST_F(PixbufTest, creatingFromSvgUriWithoutViewboxOrWidthAndHeightReturnsNull) +{ + std::string uri_data = "image/svg+xml;base64," + base64of(""); + double default_dpi = 96.0; + + ASSERT_EQ(Inkscape::Pixbuf::create_from_data_uri(uri_data.c_str(), default_dpi), nullptr); +} \ No newline at end of file diff --git a/testfiles/src/color-profile-test.cpp b/testfiles/src/color-profile-test.cpp new file mode 100644 index 0000000..b140b3c --- /dev/null +++ b/testfiles/src/color-profile-test.cpp @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Unit tests for color profile. + * + * Author: + * Jon A. Cruz + * + * Copyright (C) 2015 Authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include "gtest/gtest.h" + +#include "attributes.h" +#include "cms-system.h" +#include "object/color-profile.h" +#include "doc-per-case-test.h" + +namespace { + +/** + * Test fixture to inherit a shared doc and create a color profile instance per test. + */ +class ProfTest : public DocPerCaseTest +{ +public: + ProfTest() : + DocPerCaseTest(), + _prof(0) + { + } + +protected: + void SetUp() override + { + DocPerCaseTest::SetUp(); + _prof = new Inkscape::ColorProfile(); + ASSERT_TRUE( _prof != NULL ); + _prof->document = _doc.get(); + } + + void TearDown() override + { + if (_prof) { + delete _prof; + _prof = NULL; + } + DocPerCaseTest::TearDown(); + } + + Inkscape::ColorProfile *_prof; +}; + +typedef ProfTest ColorProfileTest; + +TEST_F(ColorProfileTest, SetRenderingIntent) +{ + struct { + gchar const *attr; + guint intVal; + } + const cases[] = { + {"auto", (guint)Inkscape::RENDERING_INTENT_AUTO}, + {"perceptual", (guint)Inkscape::RENDERING_INTENT_PERCEPTUAL}, + {"relative-colorimetric", (guint)Inkscape::RENDERING_INTENT_RELATIVE_COLORIMETRIC}, + {"saturation", (guint)Inkscape::RENDERING_INTENT_SATURATION}, + {"absolute-colorimetric", (guint)Inkscape::RENDERING_INTENT_ABSOLUTE_COLORIMETRIC}, + {"something-else", (guint)Inkscape::RENDERING_INTENT_UNKNOWN}, + {"auto2", (guint)Inkscape::RENDERING_INTENT_UNKNOWN}, + }; + + for (auto i : cases) { + _prof->setKeyValue( SPAttr::RENDERING_INTENT, i.attr); + ASSERT_EQ( (guint)i.intVal, _prof->rendering_intent ) << i.attr; + } +} + +TEST_F(ColorProfileTest, SetLocal) +{ + gchar const* cases[] = { + "local", + "something", + }; + + for (auto & i : cases) { + _prof->setKeyValue( SPAttr::LOCAL, i); + ASSERT_TRUE( _prof->local != NULL ); + if ( _prof->local ) { + ASSERT_EQ( std::string(i), _prof->local ); + } + } + _prof->setKeyValue( SPAttr::LOCAL, NULL); + ASSERT_EQ( (gchar*)0, _prof->local ); +} + +TEST_F(ColorProfileTest, SetName) +{ + gchar const* cases[] = { + "name", + "something", + }; + + for (auto & i : cases) { + _prof->setKeyValue( SPAttr::NAME, i); + ASSERT_TRUE( _prof->name != NULL ); + if ( _prof->name ) { + ASSERT_EQ( std::string(i), _prof->name ); + } + } + _prof->setKeyValue( SPAttr::NAME, NULL ); + ASSERT_EQ( (gchar*)0, _prof->name ); +} + + +} // namespace + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/testfiles/src/curve-test.cpp b/testfiles/src/curve-test.cpp new file mode 100644 index 0000000..4c73496 --- /dev/null +++ b/testfiles/src/curve-test.cpp @@ -0,0 +1,277 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** @file + * Curve test + *//* + * Authors: see git history + * + * Copyright (C) 2020 Authors + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ +#include + +#include "display/curve.h" +#include <2geom/curves.h> +#include <2geom/path.h> +#include <2geom/pathvector.h> + +class CurveTest : public ::testing::Test { + public: + Geom::Path path1; + Geom::Path path2; + Geom::Path path3; + Geom::Path path4; + + protected: + CurveTest() + : path4(Geom::Point(3, 5)) // Just a moveto + { + // Closed path + path1.append(Geom::LineSegment(Geom::Point(0, 0), Geom::Point(1, 0))); + path1.append(Geom::LineSegment(Geom::Point(1, 0), Geom::Point(1, 1))); + path1.close(); + // Closed path (ClosingSegment is zero length) + path2.append(Geom::LineSegment(Geom::Point(2, 0), Geom::Point(3, 0))); + path2.append(Geom::CubicBezier(Geom::Point(3, 0), Geom::Point(2, 1), Geom::Point(1, 1), Geom::Point(2, 0))); + path2.close(); + // Open path + path3.setStitching(true); + path3.append(Geom::EllipticalArc(Geom::Point(4, 0), 1, 2, M_PI, false, false, Geom::Point(5, 1))); + path3.append(Geom::LineSegment(Geom::Point(5, 1), Geom::Point(5, 2))); + path3.append(Geom::LineSegment(Geom::Point(6, 4), Geom::Point(2, 4))); + } +}; + +TEST_F(CurveTest, testMoveSemantics) +{ + SPCurve c1; + c1.moveto(2, 3); + c1.lineto(4, 5); + + // move construction + SPCurve c2(std::move(c1)); + + ASSERT_EQ(c1.get_segment_count(), 0); + ASSERT_EQ(c2.get_segment_count(), 1); + + // move assignment + c1 = std::move(c2); + + ASSERT_EQ(c1.get_segment_count(), 1); + ASSERT_EQ(c2.get_segment_count(), 0); +} + +TEST_F(CurveTest, testGetSegmentCount) +{ + { // Zero segments + Geom::PathVector pv; + SPCurve curve(pv); + ASSERT_EQ(curve.get_segment_count(), 0u); + } + { // Zero segments + Geom::PathVector pv; + pv.push_back(Geom::Path()); + SPCurve curve(pv); + ASSERT_EQ(curve.get_segment_count(), 0u); + } + { // Individual paths + Geom::PathVector pv((Geom::Path())); + pv[0] = path1; + ASSERT_EQ(SPCurve(pv).get_segment_count(), 3u); + pv[0] = path2; + ASSERT_EQ(SPCurve(pv).get_segment_count(), 2u); + pv[0] = path3; + ASSERT_EQ(SPCurve(pv).get_segment_count(), 4u); + pv[0] = path4; + ASSERT_EQ(SPCurve(pv).get_segment_count(), 0u); + pv[0].close(); + ASSERT_EQ(SPCurve(pv).get_segment_count(), 0u); + } + { // Combination + Geom::PathVector pv; + pv.push_back(path1); + pv.push_back(path2); + pv.push_back(path3); + pv.push_back(path4); + SPCurve curve(pv); + ASSERT_EQ(curve.get_segment_count(), 9u); + } +} + +TEST_F(CurveTest, testNodesInPathForZeroSegments) +{ + { // Zero segments + Geom::PathVector pv; + SPCurve curve(pv); + ASSERT_EQ(curve.nodes_in_path(), 0u); + } + { // Zero segments + Geom::PathVector pv; + pv.push_back(Geom::Path()); + SPCurve curve(pv); + ASSERT_EQ(curve.nodes_in_path(), 1u); + } +} + +TEST_F(CurveTest, testNodesInPathForIndividualPaths) +{ + Geom::PathVector pv((Geom::Path())); + pv[0] = path1; + ASSERT_EQ(SPCurve(pv).nodes_in_path(), 3u); + pv[0] = path2; + ASSERT_EQ(SPCurve(pv).nodes_in_path(), 2u); // zero length closing segments do not increase the nodecount. + pv[0] = path3; + ASSERT_EQ(SPCurve(pv).nodes_in_path(), 5u); + pv[0] = path4; + ASSERT_EQ(SPCurve(pv).nodes_in_path(), 1u); +} + +TEST_F(CurveTest, testNodesInPathForNakedMoveToClosedPath) +{ + Geom::PathVector pv((Geom::Path())); + pv[0] = path4; // just a MoveTo + pv[0].close(); + ASSERT_EQ(SPCurve(pv).nodes_in_path(), 1u); +} + +/* +TEST_F(CurveTest, testNodesInPathForPathsCombination) +{ + Geom::PathVector pv; + pv.push_back(path1); + pv.push_back(path2); + pv.push_back(path3); + pv.push_back(path4); + SPCurve curve(pv); + ASSERT_EQ(curve.nodes_in_path(), 12u); +} +*/ + +TEST_F(CurveTest, testIsEmpty) +{ + ASSERT_TRUE(SPCurve(Geom::PathVector()).is_empty()); + ASSERT_FALSE(SPCurve(path1).is_empty()); + ASSERT_FALSE(SPCurve(path2).is_empty()); + ASSERT_FALSE(SPCurve(path3).is_empty()); + ASSERT_FALSE(SPCurve(path4).is_empty()); +} + +TEST_F(CurveTest, testIsClosed) +{ + ASSERT_FALSE(SPCurve(Geom::PathVector()).is_closed()); + Geom::PathVector pv((Geom::Path())); + ASSERT_FALSE(SPCurve(pv).is_closed()); + pv[0].close(); + ASSERT_TRUE(SPCurve(pv).is_closed()); + ASSERT_TRUE(SPCurve(path1).is_closed()); + ASSERT_TRUE(SPCurve(path2).is_closed()); + ASSERT_FALSE(SPCurve(path3).is_closed()); + ASSERT_FALSE(SPCurve(path4).is_closed()); +} + +/* +TEST_F(CurveTest, testLastFirstSegment) +{ + Geom::PathVector pv(path4); + ASSERT_EQ(SPCurve(pv).first_segment(), (void *)0); + ASSERT_EQ(SPCurve(pv).last_segment(), (void *)0); + pv[0].close(); + ASSERT_NE(SPCurve(pv).first_segment(), (void *)0); + ASSERT_NE(SPCurve(pv).last_segment(), (void *)0); +} +*/ + +TEST_F(CurveTest, testLastFirstPath) +{ + Geom::PathVector pv; + ASSERT_EQ(SPCurve(pv).first_path(), (void *)0); + ASSERT_EQ(SPCurve(pv).last_path(), (void *)0); + pv.push_back(path1); + ASSERT_EQ(*SPCurve(pv).first_path(), pv[0]); + ASSERT_EQ(*SPCurve(pv).last_path(), pv[0]); + pv.push_back(path2); + ASSERT_EQ(*SPCurve(pv).first_path(), pv[0]); + ASSERT_EQ(*SPCurve(pv).last_path(), pv[1]); + pv.push_back(path3); + ASSERT_EQ(*SPCurve(pv).first_path(), pv[0]); + ASSERT_EQ(*SPCurve(pv).last_path(), pv[2]); + pv.push_back(path4); + ASSERT_EQ(*SPCurve(pv).first_path(), pv[0]); + ASSERT_EQ(*SPCurve(pv).last_path(), pv[3]); +} + +TEST_F(CurveTest, testFirstPoint) +{ + ASSERT_EQ(*(SPCurve(path1).first_point()), Geom::Point(0, 0)); + ASSERT_EQ(*(SPCurve(path2).first_point()), Geom::Point(2, 0)); + ASSERT_EQ(*(SPCurve(path3).first_point()), Geom::Point(4, 0)); + ASSERT_EQ(*(SPCurve(path4).first_point()), Geom::Point(3, 5)); + Geom::PathVector pv; + ASSERT_FALSE(SPCurve(pv).first_point()); + pv.push_back(path1); + pv.push_back(path2); + pv.push_back(path3); + ASSERT_EQ(*(SPCurve(pv).first_point()), Geom::Point(0, 0)); + pv.insert(pv.begin(), path4); + ASSERT_EQ(*(SPCurve(pv).first_point()), Geom::Point(3, 5)); +} + +/* +TEST_F(CurveTest, testLastPoint) +{ + ASSERT_EQ(*(SPCurve(path1).last_point()), Geom::Point(0, 0)); + ASSERT_EQ(*(SPCurve(path2).last_point()), Geom::Point(2, 0)); + ASSERT_EQ(*(SPCurve(path3).last_point()), Geom::Point(8, 4)); + ASSERT_EQ(*(SPCurve(path4).last_point()), Geom::Point(3, 5)); + Geom::PathVector pv; + ASSERT_FALSE(SPCurve(pv).last_point()); + pv.push_back(path1); + pv.push_back(path2); + pv.push_back(path3); + ASSERT_EQ(*(SPCurve(pv).last_point()), Geom::Point(8, 4)); + pv.push_back(path4); + ASSERT_EQ(*(SPCurve(pv).last_point()), Geom::Point(3, 5)); +} +*/ + +TEST_F(CurveTest, testSecondPoint) +{ + ASSERT_EQ(*(SPCurve(path1).second_point()), Geom::Point(1, 0)); + ASSERT_EQ(*(SPCurve(path2).second_point()), Geom::Point(3, 0)); + ASSERT_EQ(*(SPCurve(path3).second_point()), Geom::Point(5, 1)); + ASSERT_EQ(*(SPCurve(path4).second_point()), Geom::Point(3, 5)); + Geom::PathVector pv; + pv.push_back(path1); + pv.push_back(path2); + pv.push_back(path3); + ASSERT_EQ(*(SPCurve(pv).second_point()), Geom::Point(1, 0)); + pv.insert(pv.begin(), path4); + ASSERT_EQ(*SPCurve(pv).second_point(), Geom::Point(0, 0)); +} + +/* +TEST_F(CurveTest, testPenultimatePoint) +{ + ASSERT_EQ(*(SPCurve(Geom::PathVector(path1)).penultimate_point()), Geom::Point(1, 1)); + ASSERT_EQ(*(SPCurve(Geom::PathVector(path2)).penultimate_point()), Geom::Point(3, 0)); + ASSERT_EQ(*(SPCurve(Geom::PathVector(path3)).penultimate_point()), Geom::Point(6, 4)); + ASSERT_EQ(*(SPCurve(Geom::PathVector(path4)).penultimate_point()), Geom::Point(3, 5)); + Geom::PathVector pv; + pv.push_back(path1); + pv.push_back(path2); + pv.push_back(path3); + ASSERT_EQ(*(SPCurve(pv).penultimate_point()), Geom::Point(6, 4)); + pv.push_back(path4); + ASSERT_EQ(*(SPCurve(pv).penultimate_point()), Geom::Point(8, 4)); +} +*/ + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/testfiles/src/cxxtests-to-migrate/marker-test.h b/testfiles/src/cxxtests-to-migrate/marker-test.h new file mode 100644 index 0000000..1a77aff --- /dev/null +++ b/testfiles/src/cxxtests-to-migrate/marker-test.h @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** @file + * @brief Unit tests for SVG marker handling + *//* + * Authors: + * see git history + * Johan Engelen + * + * Copyright (C) 2016 Authors + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include + +#include "sp-marker-loc.h" + +class MarkerTest : public CxxTest::TestSuite +{ +public: + + void testMarkerLoc() + { + // code depends on these *exact* values, so check them here. + TS_ASSERT_EQUALS(SP_MARKER_LOC, 0); + TS_ASSERT_EQUALS(SP_MARKER_LOC_START, 1); + TS_ASSERT_EQUALS(SP_MARKER_LOC_MID, 2); + TS_ASSERT_EQUALS(SP_MARKER_LOC_END, 3); + TS_ASSERT_EQUALS(SP_MARKER_LOC_QTY, 4); + } + +}; + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/testfiles/src/cxxtests-to-migrate/mod360-test.h b/testfiles/src/cxxtests-to-migrate/mod360-test.h new file mode 100644 index 0000000..12ee994 --- /dev/null +++ b/testfiles/src/cxxtests-to-migrate/mod360-test.h @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** @file + * TODO: insert short description here + *//* + * Authors: see git history + * + * Copyright (C) 2016 Authors + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#ifndef SEEN_MOD_360_TEST_H +#define SEEN_MOD_360_TEST_H + +#include +#include <2geom/math-utils.h> +#include "mod360.h" + + +class Mod360Test : public CxxTest::TestSuite +{ +public: + static double inf() { return INFINITY; } + static double nan() { return ((double)INFINITY) - ((double)INFINITY); } + + void testMod360() + { + double cases[][2] = { + {0, 0}, + {10, 10}, + {360, 0}, + {361, 1}, + {-1, 359}, + {-359, 1}, + {-360, -0}, + {-361, 359}, + {inf(), 0}, + {-inf(), 0}, + {nan(), 0}, + {720, 0}, + {-721, 359}, + {-1000, 80} + }; + + for ( unsigned i = 0; i < G_N_ELEMENTS(cases); i++ ) { + double result = mod360( cases[i][0] ); + TS_ASSERT_EQUALS( cases[i][1], result ); + } + } + +}; + + +#endif // SEEN_MOD_360_TEST_H + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : + diff --git a/testfiles/src/cxxtests-to-migrate/preferences-test.h b/testfiles/src/cxxtests-to-migrate/preferences-test.h new file mode 100644 index 0000000..0dc04b8 --- /dev/null +++ b/testfiles/src/cxxtests-to-migrate/preferences-test.h @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** @file + * @brief Unit tests for the Preferences object + *//* + * Authors: + * see git history + * Krzysztof KosiƄski + * + * Copyright (C) 2016 Authors + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include +#include "preferences.h" + +#include + +// test observer +class TestObserver : public Inkscape::Preferences::Observer { +public: + TestObserver(Glib::ustring const &path) : + Inkscape::Preferences::Observer(path), + value(0) {} + + virtual void notify(Inkscape::Preferences::Entry const &val) + { + value = val.getInt(); + } + int value; +}; + +class PreferencesTest : public CxxTest::TestSuite { +public: + void setUp() { + prefs = Inkscape::Preferences::get(); + } + void tearDown() { + prefs = NULL; + Inkscape::Preferences::unload(); + } + + void testStartingState() + { + TS_ASSERT_DIFFERS(prefs, static_cast(0)); + TS_ASSERT_EQUALS(prefs->isWritable(), true); + } + + void testOverwrite() + { + prefs->setInt("/test/intvalue", 123); + prefs->setInt("/test/intvalue", 321); + TS_ASSERT_EQUALS(prefs->getInt("/test/intvalue"), 321); + } + + void testDefaultReturn() + { + TS_ASSERT_EQUALS(prefs->getInt("/this/path/does/not/exist", 123), 123); + } + + void testLimitedReturn() + { + prefs->setInt("/test/intvalue", 1000); + + // simple case + TS_ASSERT_EQUALS(prefs->getIntLimited("/test/intvalue", 123, 0, 500), 123); + // the below may seem quirky but this behaviour is intended + TS_ASSERT_EQUALS(prefs->getIntLimited("/test/intvalue", 123, 1001, 5000), 123); + // corner cases + TS_ASSERT_EQUALS(prefs->getIntLimited("/test/intvalue", 123, 0, 1000), 1000); + TS_ASSERT_EQUALS(prefs->getIntLimited("/test/intvalue", 123, 1000, 5000), 1000); + } + + void testKeyObserverNotification() + { + Glib::ustring const path = "/some/random/path"; + TestObserver obs("/some/random"); + obs.value = 1; + prefs->setInt(path, 5); + TS_ASSERT_EQUALS(obs.value, 1); // no notifications sent before adding + + prefs->addObserver(obs); + prefs->setInt(path, 10); + TS_ASSERT_EQUALS(obs.value, 10); + prefs->setInt("/some/other/random/path", 10); + TS_ASSERT_EQUALS(obs.value, 10); // value should not change + + prefs->removeObserver(obs); + prefs->setInt(path, 15); + TS_ASSERT_EQUALS(obs.value, 10); // no notifications sent after removal + } + + void testEntryObserverNotification() + { + Glib::ustring const path = "/some/random/path"; + TestObserver obs(path); + obs.value = 1; + prefs->setInt(path, 5); + TS_ASSERT_EQUALS(obs.value, 1); // no notifications sent before adding + + prefs->addObserver(obs); + prefs->setInt(path, 10); + TS_ASSERT_EQUALS(obs.value, 10); + + // test that filtering works properly + prefs->setInt("/some/random/value", 1234); + TS_ASSERT_EQUALS(obs.value, 10); + prefs->setInt("/some/randomvalue", 1234); + TS_ASSERT_EQUALS(obs.value, 10); + prefs->setInt("/some/random/path2", 1234); + TS_ASSERT_EQUALS(obs.value, 10); + + prefs->removeObserver(obs); + prefs->setInt(path, 15); + TS_ASSERT_EQUALS(obs.value, 10); // no notifications sent after removal + } + + void testPreferencesEntryMethods() + { + prefs->setInt("/test/prefentry", 100); + Inkscape::Preferences::Entry val = prefs->getEntry("/test/prefentry"); + TS_ASSERT(val.isValid()); + TS_ASSERT_EQUALS(val.getPath(), "/test/prefentry"); + TS_ASSERT_EQUALS(val.getEntryName(), "prefentry"); + TS_ASSERT_EQUALS(val.getInt(), 100); + } +private: + Inkscape::Preferences *prefs; +}; + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/testfiles/src/cxxtests-to-migrate/sp-style-elem-test.h b/testfiles/src/cxxtests-to-migrate/sp-style-elem-test.h new file mode 100644 index 0000000..afc46db --- /dev/null +++ b/testfiles/src/cxxtests-to-migrate/sp-style-elem-test.h @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** @file + * TODO: insert short description here + *//* + * Authors: see git history + * + * Copyright (C) 2016 Authors + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ +#ifndef SEEN_SP_STYLE_ELEM_TEST_H +#define SEEN_SP_STYLE_ELEM_TEST_H + +#include + +#include "test-helpers.h" + +#include "sp-style-elem.h" +#include "xml/repr.h" + +class SPStyleElemTest : public CxxTest::TestSuite +{ +public: + std::unique_ptr _doc; + + SPStyleElemTest() = default; + + virtual ~SPStyleElemTest() = default; + + static void createSuiteSubclass( SPStyleElemTest *& dst ) + { + SPStyleElem *style_elem = new SPStyleElem(); + + if ( style_elem ) { + TS_ASSERT(!style_elem->is_css); + TS_ASSERT(style_elem->media.print); + TS_ASSERT(style_elem->media.screen); + delete style_elem; + + dst = new SPStyleElemTest(); + } + } + + static SPStyleElemTest *createSuite() + { + return Inkscape::createSuiteAndDocument( createSuiteSubclass ); + } + + static void destroySuite( SPStyleElemTest *suite ) { delete suite; } + +// ------------------------------------------------------------------------- +// ------------------------------------------------------------------------- + + + void testSetType() + { + SPStyleElem *style_elem = new SPStyleElem(); + style_elem->document = _doc.get(); + + style_elem->setKeyValue( SPAttr::TYPE, "something unrecognized"); + TS_ASSERT( !style_elem->is_css ); + + style_elem->setKeyValue( SPAttr::TYPE, "text/css"); + TS_ASSERT( style_elem->is_css ); + + style_elem->setKeyValue( SPAttr::TYPE, "atext/css"); + TS_ASSERT( !style_elem->is_css ); + + style_elem->setKeyValue( SPAttr::TYPE, "text/cssx"); + TS_ASSERT( !style_elem->is_css ); + + delete style_elem; + } + + void testWrite() + { + TS_ASSERT( _doc ); + TS_ASSERT( _doc->getReprDoc() ); + if ( !_doc->getReprDoc() ) { + return; // evil early return + } + + SPStyleElem *style_elem = new SPStyleElem(); + style_elem->document = _doc.get(); + + style_elem->setKeyValue( SPAttr::TYPE, "text/css"); + Inkscape::XML::Node *repr = _doc->getReprDoc()->createElement("svg:style"); + style_elem->updateRepr(_doc->getReprDoc(), repr, SP_OBJECT_WRITE_ALL); + { + gchar const *typ = repr->attribute("type"); + TS_ASSERT( typ != NULL ); + if ( typ ) + { + TS_ASSERT_EQUALS( std::string(typ), std::string("text/css") ); + } + } + + delete style_elem; + } + + void testBuild() + { + TS_ASSERT( _doc ); + TS_ASSERT( _doc->getReprDoc() ); + if ( !_doc->getReprDoc() ) { + return; // evil early return + } + + SPStyleElem *style_elem = new SPStyleElem(); + Inkscape::XML::Node *const repr = _doc->getReprDoc()->createElement("svg:style"); + repr->setAttribute("type", "text/css"); + style_elem->invoke_build( _doc.get(), repr, false); + TS_ASSERT( style_elem->is_css ); + TS_ASSERT( style_elem->media.print ); + TS_ASSERT( style_elem->media.screen ); + + /* Some checks relevant to the read_content test below. */ + { + g_assert(_doc->style_cascade); + CRStyleSheet const *const stylesheet = cr_cascade_get_sheet(_doc->style_cascade, ORIGIN_AUTHOR); + g_assert(stylesheet); + g_assert(stylesheet->statements == NULL); + } + + delete style_elem; + Inkscape::GC::release(repr); + } + + void testReadContent() + { + TS_ASSERT( _doc ); + TS_ASSERT( _doc->getReprDoc() ); + if ( !_doc->getReprDoc() ) { + return; // evil early return + } + + SPStyleElem *style_elem = new SPStyleElem(); + Inkscape::XML::Node *const repr = _doc->getReprDoc()->createElement("svg:style"); + repr->setAttribute("type", "text/css"); + Inkscape::XML::Node *const content_repr = _doc->getReprDoc()->createTextNode(".myclass { }"); + repr->addChild(content_repr, NULL); + style_elem->invoke_build(_doc.get(), repr, false); + TS_ASSERT( style_elem->is_css ); + TS_ASSERT( _doc->style_cascade ); + CRStyleSheet const *const stylesheet = cr_cascade_get_sheet(_doc->style_cascade, ORIGIN_AUTHOR); + TS_ASSERT(stylesheet != NULL); + TS_ASSERT(stylesheet->statements != NULL); + + delete style_elem; + Inkscape::GC::release(repr); + } + +}; + + +#endif // SEEN_SP_STYLE_ELEM_TEST_H + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/testfiles/src/cxxtests-to-migrate/test-helpers.h b/testfiles/src/cxxtests-to-migrate/test-helpers.h new file mode 100644 index 0000000..a6e49e5 --- /dev/null +++ b/testfiles/src/cxxtests-to-migrate/test-helpers.h @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** @file + * TODO: insert short description here + *//* + * Authors: see git history + * + * Copyright (C) 2016 Authors + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ +#ifndef SEEN_TEST_HELPERS_H +#define SEEN_TEST_HELPERS_H + + +#include + +#include "document.h" +#include "inkscape.h" + + +// Dummy functions to keep linker happy +#if !defined(DUMMY_MAIN_TEST_CALLS_SEEN) +#define DUMMY_MAIN_TEST_CALLS_SEEN +int sp_main_gui (int, char const**) { return 0; } +int sp_main_console (int, char const**) { return 0; } +#endif // DUMMY_MAIN_TEST_CALLS_SEEN + +namespace Inkscape +{ + +template +T* createSuiteAndDocument( void (*fun)(T*&) ) +{ + T* suite = 0; + +#if !GLIB_CHECK_VERSION(2,36,0) + g_type_init(); +#endif + + Inkscape::GC::init(); + if ( !Inkscape::Application::exists() ) + { + // Create the global inkscape object. + Inkscape::Application::create(false); + } + + auto tmp = std::unique_ptr(SPDocument::createNewDoc(NULL, TRUE, true)); + if ( tmp ) { + fun( suite ); + if ( suite ) + { + suite->_doc = std::move(tmp); + } + } + + return suite; +} + +} // namespace Inkscape + +#endif // SEEN_TEST_HELPERS_H + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/testfiles/src/cxxtests-to-migrate/verbs-test.h b/testfiles/src/cxxtests-to-migrate/verbs-test.h new file mode 100644 index 0000000..b8fd299 --- /dev/null +++ b/testfiles/src/cxxtests-to-migrate/verbs-test.h @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** @file + * TODO: insert short description here + *//* + * Authors: see git history + * + * Copyright (C) 2016 Authors + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + + +#include + +#include "verbs.h" + +class VerbsTest : public CxxTest::TestSuite +{ +public: + + class TestHook : public Inkscape::Verb { + public: + static int getInternalTableSize() { return _getBaseListSize(); } + + private: + TestHook(); + }; + + void testEnumLength() + { + TS_ASSERT_DIFFERS( 0, static_cast(SP_VERB_LAST) ); + TS_ASSERT_EQUALS( static_cast(SP_VERB_LAST) + 1, TestHook::getInternalTableSize() ); + } + + void testEnumFixed() + { + TS_ASSERT_EQUALS( 0, static_cast(SP_VERB_INVALID) ); + TS_ASSERT_EQUALS( 1, static_cast(SP_VERB_NONE) ); + + TS_ASSERT_DIFFERS( 0, static_cast(SP_VERB_LAST) ); + TS_ASSERT_DIFFERS( 1, static_cast(SP_VERB_LAST) ); + } + + void testFetch() + { + for ( int i = 0; i < static_cast(SP_VERB_LAST); i++ ) + { + char tmp[16]; + snprintf( tmp, sizeof(tmp), "Verb# %d", i ); + tmp[sizeof(tmp)-1] = 0; + std::string descr(tmp); + + Inkscape::Verb* verb = Inkscape::Verb::get(i); + TSM_ASSERT( descr, verb ); + if ( verb ) + { + TSM_ASSERT_EQUALS( descr, verb->get_code(), static_cast(i) ); + + if ( i != static_cast(SP_VERB_INVALID) ) + { + TSM_ASSERT( descr, verb->get_id() ); + TSM_ASSERT( descr, verb->get_name() ); + + Inkscape::Verb* bounced = verb->getbyid( verb->get_id() ); + // TODO - put this back once verbs are fixed + //TSM_ASSERT( descr, bounced ); + if ( bounced ) + { + TSM_ASSERT_EQUALS( descr, bounced->get_code(), static_cast(i) ); + } + else + { + TS_FAIL( std::string("Unable to getbyid() for ") + descr + std::string(" ID: '") + std::string(verb->get_id()) + std::string("'") ); + } + } + else + { + TSM_ASSERT( std::string("SP_VERB_INVALID"), !verb->get_id() ); + TSM_ASSERT( std::string("SP_VERB_INVALID"), !verb->get_name() ); + } + } + } + } + +}; + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/testfiles/src/dir-util-test.cpp b/testfiles/src/dir-util-test.cpp new file mode 100644 index 0000000..bac5e3c --- /dev/null +++ b/testfiles/src/dir-util-test.cpp @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Unit tests for dir utils. + * + * Author: + * Jon A. Cruz + * + * Copyright (C) 2015 Authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include "gtest/gtest.h" + +#include + +#include "io/dir-util.h" + +namespace { + + +TEST(DirUtilTest, Base) +{ + char const* cases[][3] = { +#if defined(WIN32) || defined(__WIN32__) + {"\\foo\\bar", "\\foo", "bar"}, + {"\\foo\\barney", "\\foo\\bar", "\\foo\\barney"}, + {"\\foo\\bar\\baz", "\\foo\\", "bar\\baz"}, + {"\\foo\\bar\\baz", "\\", "foo\\bar\\baz"}, + {"\\foo\\bar\\baz", "\\foo\\qux", "\\foo\\bar\\baz"}, +#else + {"/foo/bar", "/foo", "bar"}, + {"/foo/barney", "/foo/bar", "/foo/barney"}, + {"/foo/bar/baz", "/foo/", "bar/baz"}, + {"/foo/bar/baz", "/", "foo/bar/baz"}, + {"/foo/bar/baz", "/foo/qux", "/foo/bar/baz"}, +#endif + }; + + for (auto & i : cases) + { + if ( i[0] && i[1] ) { // std::string can't use null. + std::string result = sp_relative_path_from_path( i[0], i[1] ); + ASSERT_FALSE( result.empty() ); + if ( !result.empty() ) + { + ASSERT_EQ( std::string(i[2]), result ); + } + } + } +} + +} // namespace + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/testfiles/src/drag-and-drop-svgz.cpp b/testfiles/src/drag-and-drop-svgz.cpp new file mode 100644 index 0000000..faa3e0f --- /dev/null +++ b/testfiles/src/drag-and-drop-svgz.cpp @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT +/** + * @file + * Test that svgz (= compressed SVG) import/drag-and-drop + * is working: https://gitlab.com/inkscape/inkscape/-/issues/906 . + * + */ +/* + * Authors: + * Shlomi Fish + * + * Copyright (C) 2020 Authors + */ + +#include "doc-per-case-test.h" +#include + +#include "extension/init.h" +#include "extension/db.h" +#include "extension/find_extension_by_mime.h" +#include "extension/internal/svgz.h" +#include "io/resource.h" +#include "path-prefix.h" +#include "preferences.h" + +#include "gtest/gtest.h" + +#include +class SvgzImportTest : public DocPerCaseTest { + public: + void TestBody() override + { + Inkscape::Extension::init(); + ASSERT_TRUE(_doc != nullptr); + ASSERT_TRUE(_doc->getRoot() != nullptr); + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + prefs->setBool("/dialogs/import/ask_svg", true); + prefs->setBool("/options/onimport", true); + auto ext = Inkscape::Extension::find_by_mime("image/svg+xml-compressed"); + if (!ext) { + std::cerr << "SvgzImportTest: Failed to find mime type!" << std::endl; + } + ext->set_gui(true); + + using namespace Inkscape::IO::Resource; + auto fn = get_path_string(SYSTEM, EXAMPLES, "tiger.svgz"); + + auto imod = dynamic_cast(ext); + auto svg_mod = (new Inkscape::Extension::Internal::Svg); + ASSERT_TRUE(svg_mod->open(imod, fn.c_str()) != nullptr); + } + ~SvgzImportTest() override {} +}; + +TEST_F(SvgzImportTest, Eq) +{ + SvgzImportTest foo; + foo.TestBody(); +} + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/testfiles/src/drawing-pattern-test.cpp b/testfiles/src/drawing-pattern-test.cpp new file mode 100644 index 0000000..c395169 --- /dev/null +++ b/testfiles/src/drawing-pattern-test.cpp @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** + * @file + * Test drawing_pattern_test + */ +/* + * Authors: + * PBS + * + * Copyright (C) 2022 Authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ +#include + +#include +#include <2geom/int-rect.h> +#include <2geom/int-point.h> + +#include "inkscape.h" +#include "document.h" +#include "object/sp-root.h" +#include "display/drawing.h" +#include "display/drawing-surface.h" +#include "display/drawing-context.h" + +TEST(DrawingPatternTest, fragments) +{ + if (!Inkscape::Application::exists()) { + Inkscape::Application::create(false); + } + + auto doc = std::unique_ptr(SPDocument::createNewDoc(INKSCAPE_TESTS_DIR "/rendering_tests/drawing-pattern-test.svg", false)); + ASSERT_TRUE((bool)doc); + ASSERT_TRUE((bool)doc->getRoot()); + + doc->ensureUpToDate(); + + class Display + { + public: + Display(SPDocument *doc) { + root = doc->getRoot(); + dkey = SPItem::display_key_new(1); + rootitem = root->invoke_show(drawing, dkey, SP_ITEM_SHOW_DISPLAY); + drawing.setRoot(rootitem); + drawing.update(); + } + + ~Display() + { + root->invoke_hide(dkey); + } + + auto draw(Geom::IntRect const &rect) + { + auto cs = Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32, rect.width(), rect.height()); + auto ds = Inkscape::DrawingSurface(cs->cobj(), rect.min()); + auto dc = Inkscape::DrawingContext(ds); + drawing.render(dc, rect); + return cs; + } + + private: + Inkscape::Drawing drawing; + SPRoot *root; + Inkscape::DrawingItem *rootitem; + unsigned dkey; + }; + + auto const tile = Geom::IntPoint(30, 30); + auto const area = Geom::IntRect::from_xywh(0, 0, 100, 100); + + auto const reference = Display(doc.get()).draw(area); + + uint32_t state = 0; + auto rand = [&] { + state = (state * 1103515245) + 12345; + return state; + }; + rand(); + + auto randrect = [&] { + int w = rand() % tile.x() / 3 + 1; + int h = rand() % tile.y() / 3 + 1; + int x = rand() % (area.width() - w + 1); + int y = rand() % (area.height() - h + 1); + return Geom::IntRect::from_xywh(x, y, w, h); + }; + + int maxdiff = 0; + auto compare = [&] (Cairo::RefPtr const &part, Geom::IntPoint const &off) { + for (int y = 0; y < part->get_height(); y++) { + auto p = reference->get_data() + (off.y() + y) * reference->get_stride() + off.x() * 4; + auto q = part->get_data() + y * part->get_stride(); + for (int x = 0; x < part->get_width(); x++) { + for (int c = 0; c < 4; c++) { + auto diff = std::abs((int)p[c] - (int)q[c]); + maxdiff = std::max(maxdiff, diff); + } + p += 4; + q += 4; + } + } + }; + + for (int j = 0; j < 5; j++) { + auto d = Display(doc.get()); + for (int i = 0; i < 20; i++) { + auto const rect = randrect(); + auto const part = d.draw(rect); + compare(part, rect.min()); + } + } + + ASSERT_LE(maxdiff, 10); +} diff --git a/testfiles/src/extract-uri-test.cpp b/testfiles/src/extract-uri-test.cpp new file mode 100644 index 0000000..acff966 --- /dev/null +++ b/testfiles/src/extract-uri-test.cpp @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** + * @file + * Test extract_uri + */ +/* + * Authors: + * Thomas Holder + * + * Copyright (C) 2018 Authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include "extract-uri.h" +#include "gtest/gtest.h" + +TEST(ExtractUriTest, valid) +{ + ASSERT_EQ(extract_uri("url(#foo)"), "#foo"); + ASSERT_EQ(extract_uri("url( \t #foo \t )"), "#foo"); + ASSERT_EQ(extract_uri("url( '#foo' )"), "#foo"); + ASSERT_EQ(extract_uri("url('url(foo)')"), "url(foo)"); + ASSERT_EQ(extract_uri("url(\"foo(url)\")"), "foo(url)"); + ASSERT_EQ(extract_uri("url()bar"), ""); + ASSERT_EQ(extract_uri("url( )bar"), ""); + ASSERT_EQ(extract_uri("url(a b)"), "a b"); +} + +TEST(ExtractUriTest, legacy) +{ + ASSERT_EQ(extract_uri("url (foo)"), "foo"); +} + +TEST(ExtractUriTest, invalid) +{ + ASSERT_EQ(extract_uri("#foo"), ""); + ASSERT_EQ(extract_uri(" url(foo)"), ""); + ASSERT_EQ(extract_uri("url(#foo"), ""); + ASSERT_EQ(extract_uri("url('#foo'"), ""); + ASSERT_EQ(extract_uri("url('#foo)"), ""); + ASSERT_EQ(extract_uri("url #foo)"), ""); +} + +static char const *extract_end(char const *s) +{ + char const *end = nullptr; + extract_uri(s, &end); + return end; +} + +TEST(ExtractUriTest, endptr) +{ + ASSERT_STREQ(extract_end(""), nullptr); + ASSERT_STREQ(extract_end("url(invalid"), nullptr); + ASSERT_STREQ(extract_end("url('invalid)"), nullptr); + ASSERT_STREQ(extract_end("url(valid)"), ""); + ASSERT_STREQ(extract_end("url(valid)foo"), "foo"); + ASSERT_STREQ(extract_end("url('valid')bar"), "bar"); + ASSERT_STREQ(extract_end("url( 'valid' )bar"), "bar"); + ASSERT_STREQ(extract_end("url( valid ) bar "), " bar "); + ASSERT_STREQ(extract_end("url()bar"), "bar"); + ASSERT_STREQ(extract_end("url( )bar"), "bar"); +} + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/testfiles/src/lpe-test.cpp b/testfiles/src/lpe-test.cpp new file mode 100644 index 0000000..b3e9786 --- /dev/null +++ b/testfiles/src/lpe-test.cpp @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** @file + * LPE tests + *//* + * Authors: see git history + * + * Copyright (C) 2020 Authors + * + * Released under GNU GPL version 2 or later, read the file 'COPYING' for more information + */ + +#include +#include +#include +#include +#include +#include +#include + +using namespace Inkscape; +using namespace Inkscape::LivePathEffect; + +class LPETest : public LPESPathsTest { +public: + void run() { + testDoc(svg); + } +}; + +// A) FILE BASED TESTS +TEST_F(LPETest, Inkscape_0_92) { run(); } +TEST_F(LPETest, Inkscape_1_0) { run(); } +TEST_F(LPETest, Inkscape_1_1) { run(); } +TEST_F(LPETest, Inkscape_1_2) { run(); } +TEST_F(LPETest, Inkscape_1_3) { run(); } +// B) CUSTOM TESTS +// BOOL LPE +TEST_F(LPETest, Bool_canBeApplyedToNonSiblingPaths) +{ + std::string svg("\ +\ + \ + \ + \ + \ + \ + \ + \ +"); + + SPDocument *doc = SPDocument::createNewDocFromMem(svg.c_str(), svg.size(), true); + doc->ensureUpToDate(); + + auto lpe_item = cast(doc->getObjectById("rect1")); + ASSERT_TRUE(lpe_item != nullptr); + + auto lpe_bool_op_effect = dynamic_cast(lpe_item->getFirstPathEffectOfType(EffectType::BOOL_OP)); + ASSERT_TRUE(lpe_bool_op_effect != nullptr); + + auto operand_path = lpe_bool_op_effect->getParameter("operand-path")->param_getSVGValue(); + auto circle = cast(doc->getObjectById(operand_path.substr(1))); + ASSERT_TRUE(circle != nullptr); +} \ No newline at end of file diff --git a/testfiles/src/lpe64-test.cpp b/testfiles/src/lpe64-test.cpp new file mode 100644 index 0000000..6bfd8a7 --- /dev/null +++ b/testfiles/src/lpe64-test.cpp @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** @file + * LPE 64B tests + * Because some issues rounding in 32B windows we move this tests only to 64B + *//* + * Authors: see git history + * + * Copyright (C) 2020 Authors + * + * Released under GNU GPL version 2 or later, read the file 'COPYING' for more information + */ + +#include +#include +#include + +using namespace Inkscape; +using namespace Inkscape::LivePathEffect; + +class LPE64Test : public LPESPathsTest { +public: + void run() { + testDoc(svg); + } +}; + +// A) FILE BASED TESTS +TEST_F(LPE64Test, Inkscape_0_92_64) { run(); } +TEST_F(LPE64Test, Inkscape_1_0_64) { run(); } +// B) CUSTOM TESTS \ No newline at end of file diff --git a/testfiles/src/min-bbox-test.cpp b/testfiles/src/min-bbox-test.cpp new file mode 100644 index 0000000..ccacd62 --- /dev/null +++ b/testfiles/src/min-bbox-test.cpp @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include <2geom/convex-hull.h> +#include <2geom/transforms.h> +#include +#include + +// Get the axis-aligned bouding box of a set of points, transforming by affine first. +auto aligned_bbox(std::vector const &pts, Geom::Affine const &affine = Geom::identity()) +{ + Geom::OptRect rect; + for (auto &pt : pts) { + rect.expandTo(pt * affine); + } + return rect; +} + +double area(Geom::OptRect const &rect) +{ + return rect ? rect->area() : 0.0; +} + +// Get an approximation to the minimum bouding box area. +double approx_min(std::vector const &pts) +{ + int constexpr N = 100; + + double min = std::numeric_limits::max(); + + for (int i = 0; i < N; i++) { + auto t = (double)i / N * M_PI * 0.5; + min = std::min(min, area(aligned_bbox(pts, Geom::Rotate(t)))); + } + + return min; +} + +// Get a crude random double. +double ranf() +{ + int constexpr N = 1000; + return (double)(rand() % N) / N; +} + +// Get a random collection of points. +auto randpts() +{ + std::vector pts; + + int count = 5 + (rand() % 10); + for (int i = 0; i < count; i++) { + pts.emplace_back(ranf(), ranf()); + } + + return pts; +} + +TEST(MinBBoxTest, random) +{ + for (int i = 0; i < 100; i++) { + auto const pts = randpts(); + auto [affine, rect] = min_bounding_box(pts); + + ASSERT_TRUE(affine.isRotation()); + + auto rect2 = aligned_bbox(pts, affine); + for (int i = 0; i < 2; i++) { + ASSERT_NEAR(rect.min()[i], rect2->min()[i], 1e-5); + ASSERT_NEAR(rect.max()[i], rect2->max()[i], 1e-5); + } + + ASSERT_LE(rect.area(), approx_min(pts)); + } +} diff --git a/testfiles/src/object-set-test.cpp b/testfiles/src/object-set-test.cpp new file mode 100644 index 0000000..878e6ef --- /dev/null +++ b/testfiles/src/object-set-test.cpp @@ -0,0 +1,706 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Multiindex container for selection + * + * Authors: + * Adrian Boguszewski + * + * Copyright (C) 2016 Adrian Boguszewski + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#include <2geom/transforms.h> +using namespace Inkscape; +using namespace Inkscape::XML; + +class ObjectSetTest: public DocPerCaseTest { +public: + ObjectSetTest() { + auto *const _doc = this->_doc.get(); + N = _doc->getRoot()->children.size(); + + A = new SPObject(); + B = new SPObject(); + C = new SPObject(); + D = new SPObject(); + E = new SPObject(); + F = new SPObject(); + G = new SPObject(); + H = new SPObject(); + X = new SPObject(); + set = new ObjectSet(_doc); + set2 = new ObjectSet(_doc); + auto sd = _doc->getReprDoc(); + auto xt = new TextNode(Util::share_string("x"), sd); + auto ht = new TextNode(Util::share_string("h"), sd); + auto gt = new TextNode(Util::share_string("g"), sd); + auto ft = new TextNode(Util::share_string("f"), sd); + auto et = new TextNode(Util::share_string("e"), sd); + auto dt = new TextNode(Util::share_string("d"), sd); + auto ct = new TextNode(Util::share_string("c"), sd); + auto bt = new TextNode(Util::share_string("b"), sd); + auto at = new TextNode(Util::share_string("a"), sd); + X->invoke_build(_doc, xt, 0); + H->invoke_build(_doc, ht, 0); + G->invoke_build(_doc, gt, 0); + F->invoke_build(_doc, ft, 0); + E->invoke_build(_doc, et, 0); + D->invoke_build(_doc, dt, 0); + C->invoke_build(_doc, ct, 0); + B->invoke_build(_doc, bt, 0); + A->invoke_build(_doc, at, 0); + + //create 3 rects at root of document + Inkscape::XML::Node *repr = _doc->getReprDoc()->createElement("svg:rect"); + _doc->getRoot()->appendChild(repr); + r1.reset(cast(_doc->getObjectByRepr(repr))); + repr = _doc->getReprDoc()->createElement("svg:rect"); + _doc->getRoot()->appendChild(repr); + r2.reset(cast(_doc->getObjectByRepr(repr))); + repr = _doc->getReprDoc()->createElement("svg:rect"); + _doc->getRoot()->appendChild(repr); + r3.reset(cast(_doc->getObjectByRepr(repr))); + EXPECT_EQ(N + 3, _doc->getRoot()->children.size());// defs, namedview, and those three rects. + r1->x = r1->y = r2->x = r2->y = r3->x = r3->y = 0; + r1->width = r1->height = r2->width = r2->height = r3->width = r3->height = 10; + r1->set_shape(); + r2->set_shape(); + r3->set_shape(); + + } + ~ObjectSetTest() override { + delete set; + delete set2; + delete X; + delete H; + delete G; + delete F; + delete E; + delete D; + delete C; + delete B; + delete A; + } + SPObject* A; + SPObject* B; + SPObject* C; + SPObject* D; + SPObject* E; + SPObject* F; + SPObject* G; + SPObject* H; + SPObject* X; + std::unique_ptr r1; + std::unique_ptr r2; + std::unique_ptr r3; + ObjectSet* set; + ObjectSet* set2; + int N; //!< Number of root children in default document +}; + +bool containsClone(ObjectSet* set) { + for (auto it : set->items()) { + if (is(it)) { + return true; + } + if (is(it)) { + ObjectSet tmp_set(set->document()); + std::vector c = it->childList(false); + tmp_set.setList(c); + if (containsClone(&tmp_set)) { + return true; + } + } + } + return false; +} + +TEST_F(ObjectSetTest, Basics) { + EXPECT_EQ(0, set->size()); + set->add(A); + EXPECT_EQ(1, set->size()); + EXPECT_TRUE(set->includes(A)); + EXPECT_TRUE(set->includes(A->getRepr())); + set->add(B); + set->add(C); + EXPECT_EQ(3, set->size()); + EXPECT_TRUE(set->includes(B)); + EXPECT_TRUE(set->includes(C)); + EXPECT_FALSE(set->includes(D)); + EXPECT_FALSE(set->includes(D->getRepr())); + EXPECT_FALSE(set->includes(X)); + EXPECT_FALSE(set->includes((SPObject*)nullptr)); + EXPECT_FALSE(set->includes((Inkscape::XML::Node*)nullptr)); + set->remove(A); + EXPECT_EQ(2, set->size()); + EXPECT_FALSE(set->includes(A)); + set->clear(); + EXPECT_EQ(0, set->size()); + bool resultNull = set->add((SPObject*)nullptr); + EXPECT_FALSE(resultNull); + EXPECT_EQ(0, set->size()); + bool resultNull2 = set->remove(nullptr); + EXPECT_FALSE(resultNull2); +} + +TEST_F(ObjectSetTest, Advanced) { + set->add(A); + set->add(B); + set->add(C); + EXPECT_TRUE(set->includes(C)); + set->toggle(C); + EXPECT_EQ(2, set->size()); + EXPECT_FALSE(set->includes(C)); + set->toggle(D); + EXPECT_EQ(3, set->size()); + EXPECT_TRUE(set->includes(D)); + set->toggle(D); + EXPECT_EQ(2, set->size()); + EXPECT_FALSE(set->includes(D)); + EXPECT_EQ(nullptr, set->single()); + set->set(X); + EXPECT_EQ(1, set->size()); + EXPECT_TRUE(set->includes(X)); + EXPECT_EQ(X, set->single()); + EXPECT_FALSE(set->isEmpty()); + set->clear(); + EXPECT_TRUE(set->isEmpty()); + std::vector list1 {A, B, C, D}; + std::vector list2 {E, F}; + set->addList(list1); + EXPECT_EQ(4, set->size()); + set->addList(list2); + EXPECT_EQ(6, set->size()); + EXPECT_TRUE(set->includes(A)); + EXPECT_TRUE(set->includes(B)); + EXPECT_TRUE(set->includes(C)); + EXPECT_TRUE(set->includes(D)); + EXPECT_TRUE(set->includes(E)); + EXPECT_TRUE(set->includes(F)); + set->setList(list2); + EXPECT_EQ(2, set->size()); + EXPECT_TRUE(set->includes(E)); + EXPECT_TRUE(set->includes(F)); +} + +TEST_F(ObjectSetTest, Items) { + // cannot test smallestItem and largestItem functions due to too many dependencies + // uncomment if the problem is fixed + + SPRect* rect10x100 = &*r1; + rect10x100->x = rect10x100->x = 0; + rect10x100->width = 10; + rect10x100->height = 100; + rect10x100->set_shape(); + + SPRect* rect20x40 = &*r2; + rect20x40->x = rect20x40->x = 0; + rect20x40->width = 20; + rect20x40->height = 40; + rect20x40->set_shape(); + + SPRect* rect30x30 = &*r3; + rect30x30->x = rect30x30->x = 0; + rect30x30->width = 30; + rect30x30->height = 30; + rect30x30->set_shape(); + + + set->add(rect10x100); + EXPECT_EQ(rect10x100, set->singleItem()); + EXPECT_EQ(rect10x100->getRepr(), set->singleRepr()); + set->add(rect20x40); + EXPECT_EQ(nullptr, set->singleItem()); + EXPECT_EQ(nullptr, set->singleRepr()); + set->add(rect30x30); + EXPECT_EQ(3, set->size()); + EXPECT_EQ(rect10x100, set->smallestItem(ObjectSet::CompareSize::HORIZONTAL)); + EXPECT_EQ(rect30x30, set->smallestItem(ObjectSet::CompareSize::VERTICAL)); + EXPECT_EQ(rect20x40, set->smallestItem(ObjectSet::CompareSize::AREA)); + EXPECT_EQ(rect30x30, set->largestItem(ObjectSet::CompareSize::HORIZONTAL)); + EXPECT_EQ(rect10x100, set->largestItem(ObjectSet::CompareSize::VERTICAL)); + EXPECT_EQ(rect10x100, set->largestItem(ObjectSet::CompareSize::AREA)); +} + +TEST_F(ObjectSetTest, Ranges) { + std::vector objs {A, D, B, E, C, F}; + set->add(objs.begin() + 1, objs.end() - 1); + EXPECT_EQ(4, set->size()); + auto it = set->objects().begin(); + EXPECT_EQ(D, *it++); + EXPECT_EQ(B, *it++); + EXPECT_EQ(E, *it++); + EXPECT_EQ(C, *it++); + EXPECT_EQ(set->objects().end(), it); + SPObject* rect1 = SPFactory::createObject("svg:rect"); + SPObject* rect2 = SPFactory::createObject("svg:rect"); + SPObject* rect3 = SPFactory::createObject("svg:rect"); + set->add(rect1); + set->add(rect2); + set->add(rect3); + EXPECT_EQ(7, set->size()); + auto xmlNode = set->xmlNodes().begin(); + EXPECT_EQ(3, boost::distance(set->xmlNodes())); + EXPECT_EQ(rect1->getRepr(), *xmlNode++); + EXPECT_EQ(rect2->getRepr(), *xmlNode++); + EXPECT_EQ(rect3->getRepr(), *xmlNode++); + EXPECT_EQ(set->xmlNodes().end(), xmlNode); + auto item = set->items().begin(); + EXPECT_EQ(3, boost::distance(set->items())); + EXPECT_EQ(rect1, *item++); + EXPECT_EQ(rect2, *item++); + EXPECT_EQ(rect3, *item++); + EXPECT_EQ(set->items().end(), item); +} + +TEST_F(ObjectSetTest, Autoremoving) { + set->add(A); + EXPECT_TRUE(set->includes(A)); + EXPECT_EQ(1, set->size()); + A->releaseReferences(); + EXPECT_EQ(0, set->size()); +} + +TEST_F(ObjectSetTest, BasicDescendants) { + A->attach(B, nullptr); + B->attach(C, nullptr); + A->attach(D, nullptr); + bool resultB = set->add(B); + bool resultB2 = set->add(B); + EXPECT_TRUE(resultB); + EXPECT_FALSE(resultB2); + EXPECT_TRUE(set->includes(B)); + bool resultC = set->add(C); + EXPECT_FALSE(resultC); + EXPECT_FALSE(set->includes(C)); + EXPECT_EQ(1, set->size()); + bool resultA = set->add(A); + EXPECT_TRUE(resultA); + EXPECT_EQ(1, set->size()); + EXPECT_TRUE(set->includes(A)); + EXPECT_FALSE(set->includes(B)); +} + +TEST_F(ObjectSetTest, AdvancedDescendants) { + A->attach(B, nullptr); + A->attach(C, nullptr); + A->attach(X, nullptr); + B->attach(D, nullptr); + B->attach(E, nullptr); + C->attach(F, nullptr); + C->attach(G, nullptr); + C->attach(H, nullptr); + set->add(A); + bool resultF = set->remove(F); + EXPECT_TRUE(resultF); + EXPECT_EQ(4, set->size()); + EXPECT_FALSE(set->includes(F)); + EXPECT_TRUE(set->includes(B)); + EXPECT_TRUE(set->includes(G)); + EXPECT_TRUE(set->includes(H)); + EXPECT_TRUE(set->includes(X)); + bool resultF2 = set->add(F); + EXPECT_TRUE(resultF2); + EXPECT_EQ(5, set->size()); + EXPECT_TRUE(set->includes(F)); +} + +TEST_F(ObjectSetTest, Removing) { + A->attach(B, nullptr); + A->attach(C, nullptr); + A->attach(X, nullptr); + B->attach(D, nullptr); + B->attach(E, nullptr); + C->attach(F, nullptr); + C->attach(G, nullptr); + C->attach(H, nullptr); + bool removeH = set->remove(H); + EXPECT_FALSE(removeH); + set->add(A); + bool removeX = set->remove(X); + EXPECT_TRUE(removeX); + EXPECT_EQ(2, set->size()); + EXPECT_TRUE(set->includes(B)); + EXPECT_TRUE(set->includes(C)); + EXPECT_FALSE(set->includes(X)); + EXPECT_FALSE(set->includes(A)); + bool removeX2 = set->remove(X); + EXPECT_FALSE(removeX2); + EXPECT_EQ(2, set->size()); + bool removeA = set->remove(A); + EXPECT_FALSE(removeA); + EXPECT_EQ(2, set->size()); + bool removeC = set->remove(C); + EXPECT_TRUE(removeC); + EXPECT_EQ(1, set->size()); + EXPECT_TRUE(set->includes(B)); + EXPECT_FALSE(set->includes(C)); +} + +TEST_F(ObjectSetTest, TwoSets) { + A->attach(B, nullptr); + A->attach(C, nullptr); + set->add(A); + set2->add(A); + EXPECT_EQ(1, set->size()); + EXPECT_EQ(1, set2->size()); + set->remove(B); + EXPECT_EQ(1, set->size()); + EXPECT_TRUE(set->includes(C)); + EXPECT_EQ(1, set2->size()); + EXPECT_TRUE(set2->includes(A)); + C->releaseReferences(); + EXPECT_EQ(0, set->size()); + EXPECT_EQ(1, set2->size()); + EXPECT_TRUE(set2->includes(A)); +} + +TEST_F(ObjectSetTest, SetRemoving) { + ObjectSet *objectSet = new ObjectSet(_doc.get()); + A->attach(B, nullptr); + objectSet->add(A); + objectSet->add(C); + EXPECT_EQ(2, objectSet->size()); + delete objectSet; + EXPECT_STREQ(nullptr, A->getId()); + EXPECT_STREQ(nullptr, C->getId()); +} + +TEST_F(ObjectSetTest, Delete) { + //we cannot use the same item as in other tests since it will be freed at the test destructor + + EXPECT_EQ(_doc->getRoot(), r1->parent); + set->add(r1.get()); + set->deleteItems(); + r1.release(); + EXPECT_EQ(0, set->size()); + //EXPECT_EQ(nullptr, r1->parent); +} + +TEST_F(ObjectSetTest, Ops) { + set->add(r1.get()); + set->add(r2.get()); + set->add(r3.get()); + set->duplicate(); + EXPECT_EQ(N + 6, _doc->getRoot()->children.size());// defs, namedview, and those 3x2 rects. + EXPECT_EQ(3, set->size()); + EXPECT_FALSE(set->includes(r1.get())); + set->deleteItems(); + EXPECT_TRUE(set->isEmpty()); + set->add(r1.get()); + set->add(r2.get()); + set->add(r3.get()); + set->group();//r1-3 are now invalid (grouping makes copies) + r1.release(); + r2.release(); + r3.release(); + EXPECT_EQ(N + 1, _doc->getRoot()->children.size()); + EXPECT_EQ(1, set->size()); + set->ungroup(); + EXPECT_EQ(N + 3, _doc->getRoot()->children.size()); + EXPECT_EQ(3, set->size()); + /* Uncomment this when toNextLayer is made desktop-independent + set->group(); + set2->add(set->singleItem()->childList(false)[0]); + EXPECT_EQ(3, set->singleItem()->children.size()); + EXPECT_EQ(4, _doc->getRoot()->children.size()); + set2->popFromGroup(); + EXPECT_EQ(2, set->singleItem()->children.size()); + EXPECT_EQ(5, _doc->getRoot()->children.size()); + set->ungroup(); + set->add(set2->singleItem()); + */ + set->clone(); + EXPECT_EQ(N + 6, _doc->getRoot()->children.size()); + EXPECT_EQ(3, set->size()); + EXPECT_NE(nullptr,cast(*(set->items().begin()))); + EXPECT_EQ(nullptr,cast(*(set->items().begin()))); + set->unlink(); + EXPECT_EQ(N + 6, _doc->getRoot()->children.size()); + EXPECT_EQ(3, set->size()); + EXPECT_EQ(nullptr,cast(*(set->items().begin()))); + EXPECT_NE(nullptr,cast(*(set->items().begin()))); + set->clone(); //creates 3 clones + set->clone(); //creates 3 clones of clones + EXPECT_EQ(N + 12, _doc->getRoot()->children.size()); + EXPECT_EQ(3, set->size()); + EXPECT_NE(nullptr,cast( ((SPUse*)(*(set->items().begin())))->get_original()));//"original is a Use" + set->unlink(); //clone of clone of rect -> rect + EXPECT_EQ(nullptr,cast(*(set->items().begin()))); + EXPECT_NE(nullptr,cast(*(set->items().begin()))); + set->clone(); + set->set(*(set->items().begin())); + set->cloneOriginal();//get clone original + EXPECT_EQ(N + 15, _doc->getRoot()->children.size()); + EXPECT_EQ(1, set->size()); + EXPECT_NE(nullptr,cast(*(set->items().begin()))); + //let's stop here. + // TODO: write a hundred more tests to check clone (non-)displacement when grouping, ungrouping and unlinking... + TearDownTestCase(); + SetUpTestCase(); +} + +TEST_F(ObjectSetTest, unlinkRecursiveBasic) { + // This is the same as the test (ObjectSetTest, Ops), but with unlinkRecursive instead of unlink. + set->set(r1.get()); + set->add(r2.get()); + set->add(r3.get()); + EXPECT_FALSE(containsClone(set)); + set->duplicate(); + EXPECT_FALSE(containsClone(set)); + EXPECT_EQ(N + 6, _doc->getRoot()->children.size());// defs, namedview, and those 3x2 rects. + EXPECT_EQ(3, set->size()); + EXPECT_FALSE(set->includes(r1.get())); + set->deleteItems(); + EXPECT_FALSE(containsClone(set)); + EXPECT_TRUE(set->isEmpty()); + set->add(r1.get()); + set->add(r2.get()); + set->add(r3.get()); + EXPECT_FALSE(containsClone(set)); + set->group();//r1-3 are now invalid (grouping makes copies) + r1.release(); + r2.release(); + r3.release(); + EXPECT_FALSE(containsClone(set)); + EXPECT_EQ(N + 1, _doc->getRoot()->children.size()); + EXPECT_EQ(1, set->size()); + set->ungroup(); + EXPECT_FALSE(containsClone(set)); + EXPECT_EQ(N + 3, _doc->getRoot()->children.size()); + EXPECT_EQ(3, set->size()); + /* Uncomment this when toNextLayer is made desktop-independent + set->group(); + set2->add(set->singleItem()->childList(false)[0]); + EXPECT_EQ(3, set->singleItem()->children.size()); + EXPECT_EQ(4, _doc->getRoot()->children.size()); + set2->popFromGroup(); + EXPECT_EQ(2, set->singleItem()->children.size()); + EXPECT_EQ(5, _doc->getRoot()->children.size()); + set->ungroup(); + set->add(set2->singleItem()); + */ + set->clone(); + EXPECT_TRUE(containsClone(set)); + EXPECT_EQ(N + 6, _doc->getRoot()->children.size()); + EXPECT_EQ(3, set->size()); + EXPECT_NE(nullptr, cast(*(set->items().begin()))); + EXPECT_EQ(nullptr, cast(*(set->items().begin()))); + set->unlinkRecursive(false, true); + EXPECT_FALSE(containsClone(set)); + EXPECT_EQ(N + 6, _doc->getRoot()->children.size()); + EXPECT_EQ(3, set->size()); + EXPECT_EQ(nullptr, cast(*(set->items().begin()))); + EXPECT_NE(nullptr, cast(*(set->items().begin()))); + set->clone(); //creates 3 clones + EXPECT_TRUE(containsClone(set)); + set->clone(); //creates 3 clones of clones + EXPECT_TRUE(containsClone(set)); + EXPECT_EQ(N + 12, _doc->getRoot()->children.size()); + EXPECT_EQ(3, set->size()); + EXPECT_NE(nullptr, cast( ((SPUse*)(*(set->items().begin())))->get_original()));//"original is a Use" + set->unlinkRecursive(false, true); //clone of clone of rect -> rect + EXPECT_FALSE(containsClone(set)); + EXPECT_EQ(nullptr, cast(*(set->items().begin()))); + EXPECT_NE(nullptr, cast(*(set->items().begin()))); + set->clone(); + EXPECT_TRUE(containsClone(set)); + set->set(*(set->items().begin())); + set->cloneOriginal();//get clone original + EXPECT_EQ(N + 15, _doc->getRoot()->children.size()); + EXPECT_EQ(1, set->size()); + EXPECT_NE(nullptr, cast(*(set->items().begin()))); + TearDownTestCase(); + SetUpTestCase(); +} + +TEST_F(ObjectSetTest, unlinkRecursiveAdvanced) { + set->set(r1.get()); + set->add(r2.get()); + set->add(r3.get()); + set->group();//r1-3 are now invalid (grouping makes copies) + r1.release(); + r2.release(); + r3.release(); + EXPECT_FALSE(containsClone(set)); + EXPECT_EQ(1, set->size()); + SPItem* original = set->singleItem(); + set->clone(); + EXPECT_TRUE(containsClone(set)); + EXPECT_EQ(1, set->size()); + set->add(original); + EXPECT_TRUE(containsClone(set)); + EXPECT_EQ(2, set->size()); + set->group(); + EXPECT_TRUE(containsClone(set)); + EXPECT_EQ(1, set->size()); + original = set->singleItem(); + set->clone(); + EXPECT_TRUE(containsClone(set)); + EXPECT_EQ(1, set->size()); + set->add(original); + EXPECT_TRUE(containsClone(set)); + EXPECT_EQ(2, set->size()); + set->group(); + EXPECT_TRUE(containsClone(set)); + EXPECT_EQ(1, set->size()); + original = set->singleItem(); + set->clone(); + EXPECT_TRUE(containsClone(set)); + EXPECT_EQ(1, set->size()); + set->add(original); + EXPECT_TRUE(containsClone(set)); + EXPECT_EQ(2, set->size()); + set->unlinkRecursive(false, true); + EXPECT_FALSE(containsClone(set)); + EXPECT_EQ(2, set->size()); + + TearDownTestCase(); + SetUpTestCase(); +} + +TEST_F(ObjectSetTest, ZOrder) { + //sp_object_compare_position_bool == true iff "r1set(r2.get()); + set->raise(); + //1 3 2 + EXPECT_TRUE(sp_object_compare_position_bool(r1.get(),r3.get())); + EXPECT_TRUE(sp_object_compare_position_bool(r3.get(),r2.get()));//! + set->set(r3.get()); + set->lower(); + //3 1 2 + EXPECT_TRUE(sp_object_compare_position_bool(r3.get(),r1.get())); + EXPECT_TRUE(sp_object_compare_position_bool(r1.get(),r2.get())); + set->raiseToTop(); + //1 2 3 + EXPECT_TRUE(sp_object_compare_position_bool(r1.get(),r2.get())); + EXPECT_TRUE(sp_object_compare_position_bool(r2.get(),r3.get())); + set->lowerToBottom(); + //3 1 2 + EXPECT_TRUE(sp_object_compare_position_bool(r3.get(),r1.get())); + EXPECT_TRUE(sp_object_compare_position_bool(r1.get(),r2.get())); +} + +TEST_F(ObjectSetTest, Combine) { + set->add(r1.get()); + set->add(r2.get()); + set->combine(); + r1.release(); + r2.release(); + EXPECT_EQ(1, set->size()); + EXPECT_EQ(N + 2, _doc->getRoot()->children.size()); + set->breakApart(); + EXPECT_EQ(2, set->size()); + EXPECT_EQ(N + 3, _doc->getRoot()->children.size()); + set->deleteItems(); + set->set(r3.get()); + set->toCurves(); + r3.release(); + auto x = set->singleItem(); + EXPECT_NE(nullptr,cast(x)); + EXPECT_EQ(nullptr,cast(x)); + set->deleteItems(); +} + +TEST_F(ObjectSetTest, Moves) { + set->add(r1.get()); + set->moveRelative(15,15); + EXPECT_EQ(15,r1->x.value); + Geom::Point p(20,20); + Geom::Scale s(2); + set->setScaleRelative(p,s); + EXPECT_EQ(10,r1->x.value); + EXPECT_EQ(20,r1->width.value); + set->toCurves(); + r1.release(); + auto x = set->singleItem(); + EXPECT_EQ(20,(*(x->documentVisualBounds()))[0].extent()); + set->rotate(180); + EXPECT_EQ(20,(*(x->documentVisualBounds()))[0].extent()); + set->deleteItems(); +} + +TEST_F(ObjectSetTest, toMarker) { + r1->x = 12; + r1->y = 34; + r1->width = 56; + r1->height = 78; + r1->set_shape(); + r1->updateRepr(); + + r2->x = 6; + r2->y = 7; + r2->width = 8; + r2->height = 9; + r2->set_shape(); + r2->updateRepr(); + + r3->x = 10; + r3->y = 10; + r3->width = 10; + r3->height = 10; + r3->set_shape(); + r3->updateRepr(); + + // add rects to set in different order than they appear in the document, + // to verify selection order independence. + set->set(r1.get()); + set->add(r3.get()); + set->add(r2.get()); + set->toMarker(); + + // original items got deleted + r1.release(); + r2.release(); + r3.release(); + + auto markers = _doc->getObjectsByElement("marker"); + ASSERT_EQ(markers.size(), 1); + + auto marker = cast(markers[0]); + ASSERT_NE(marker, nullptr); + + EXPECT_FLOAT_EQ(marker->refX.computed, 31); + EXPECT_FLOAT_EQ(marker->refY.computed, 52.5); + EXPECT_FLOAT_EQ(marker->markerWidth.computed, 62); + EXPECT_FLOAT_EQ(marker->markerHeight.computed, 105); + + auto markerchildren = marker->childList(false); + ASSERT_EQ(markerchildren.size(), 3); + + auto *markerrect1 = cast(markerchildren[0]); + auto *markerrect2 = cast(markerchildren[1]); + + ASSERT_NE(markerrect1, nullptr); + ASSERT_NE(markerrect2, nullptr); + + EXPECT_FLOAT_EQ(markerrect1->x.value, 6); + EXPECT_FLOAT_EQ(markerrect1->y.value, 27); + EXPECT_FLOAT_EQ(markerrect1->width.value, 56); + EXPECT_FLOAT_EQ(markerrect1->height.value, 78); + + EXPECT_FLOAT_EQ(markerrect2->x.value, 0); + EXPECT_FLOAT_EQ(markerrect2->y.value, 0); + EXPECT_FLOAT_EQ(markerrect2->width.value, 8); + EXPECT_FLOAT_EQ(markerrect2->height.value, 9); +} diff --git a/testfiles/src/object-style-test.cpp b/testfiles/src/object-style-test.cpp new file mode 100644 index 0000000..0223d06 --- /dev/null +++ b/testfiles/src/object-style-test.cpp @@ -0,0 +1,199 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** @file + * Combination style and object testing for cascading and flags. + *//* + * + * Authors: + * Martin Owens + * + * Copyright (C) 2018 Authors + * + * Released under GNU GPL version 2 or later, read the file 'COPYING' for more information + */ + +#include +#include + +#include +#include +#include + +using namespace Inkscape; +using namespace Inkscape::XML; + +class ObjectTest: public DocPerCaseTest { +public: + ObjectTest() { + char const *docString = "\ +\ +\ +\ + \ + \ + \ + \ + /\ + \ + \ + \ +\ +"; + doc.reset(SPDocument::createNewDocFromMem(docString, static_cast(strlen(docString)), false)); + doc->ensureUpToDate(); + } + + ~ObjectTest() override = default; + + std::unique_ptr doc; +}; + +/* + * Test basic cascade values, that they are set correctly as we'd want to see them. + */ +TEST_F(ObjectTest, Styles) { + ASSERT_TRUE(doc != nullptr); + ASSERT_TRUE(doc->getRoot() != nullptr); + + SPRoot *root = doc->getRoot(); + ASSERT_TRUE(root->getRepr() != nullptr); + ASSERT_TRUE(root->hasChildren()); + + auto one = cast(doc->getObjectById("one")); + ASSERT_TRUE(one != nullptr); + + // TODO: Fix when Inkscape preserves colour names (i.e. 'red') + EXPECT_EQ(one->style->fill.get_value(), Glib::ustring("#ff0000")); + EXPECT_EQ(one->style->stroke.get_value(), Glib::ustring("#008000")); + EXPECT_EQ(one->style->opacity.get_value(), Glib::ustring("0.5")); + EXPECT_EQ(one->style->stroke_width.get_value(), Glib::ustring("2px")); + + auto two = cast(doc->getObjectById("two")); + ASSERT_TRUE(two != nullptr); + + EXPECT_EQ(two->style->fill.get_value(), Glib::ustring("#808080")); + EXPECT_EQ(two->style->stroke.get_value(), Glib::ustring("#008000")); + EXPECT_EQ(two->style->opacity.get_value(), Glib::ustring("0.5")); + EXPECT_EQ(two->style->stroke_width.get_value(), Glib::ustring("4px")); + + auto three = cast(doc->getObjectById("three")); + ASSERT_TRUE(three != nullptr); + + EXPECT_EQ(three->style->fill.get_value(), Glib::ustring("#cccccc")); + EXPECT_EQ(three->style->stroke.get_value(), Glib::ustring("")); + EXPECT_EQ(three->style->opacity.get_value(), Glib::ustring("1")); + EXPECT_EQ(three->style->stroke_width.get_value(), Glib::ustring("2px")); + + auto four = cast(doc->getObjectById("four")); + ASSERT_TRUE(four != nullptr); + + EXPECT_EQ(four->style->fill.get_value(), Glib::ustring("#d0d0d0")); + EXPECT_EQ(four->style->stroke.get_value(), Glib::ustring("#ff0000")); + EXPECT_EQ(four->style->opacity.get_value(), Glib::ustring("0.5")); + EXPECT_EQ(four->style->stroke_width.get_value(), Glib::ustring("2px")); +} + +/* + * Test the origin flag for each of the values, should indicate where it came from. + */ +TEST_F(ObjectTest, StyleSource) { + ASSERT_TRUE(doc != nullptr); + ASSERT_TRUE(doc->getRoot() != nullptr); + + SPRoot *root = doc->getRoot(); + ASSERT_TRUE(root->getRepr() != nullptr); + ASSERT_TRUE(root->hasChildren()); + + auto one = cast(doc->getObjectById("one")); + ASSERT_TRUE(one != nullptr); + + EXPECT_EQ(one->style->fill.style_src, SPStyleSrc::STYLE_PROP); + EXPECT_EQ(one->style->stroke.style_src, SPStyleSrc::STYLE_PROP); + EXPECT_EQ(one->style->opacity.style_src, SPStyleSrc::STYLE_SHEET); + EXPECT_EQ(one->style->stroke_width.style_src, SPStyleSrc::STYLE_PROP); + + auto two = cast(doc->getObjectById("two")); + ASSERT_TRUE(two != nullptr); + + EXPECT_EQ(two->style->fill.style_src, SPStyleSrc::STYLE_SHEET); + EXPECT_EQ(two->style->stroke.style_src, SPStyleSrc::STYLE_PROP); + EXPECT_EQ(two->style->opacity.style_src, SPStyleSrc::STYLE_SHEET); + EXPECT_EQ(two->style->stroke_width.style_src, SPStyleSrc::STYLE_PROP); + + auto three = cast(doc->getObjectById("three")); + ASSERT_TRUE(three != nullptr); + + EXPECT_EQ(three->style->fill.style_src, SPStyleSrc::STYLE_PROP); + EXPECT_EQ(three->style->stroke.style_src, SPStyleSrc::STYLE_PROP); + EXPECT_EQ(three->style->opacity.style_src, SPStyleSrc::STYLE_SHEET); + EXPECT_EQ(three->style->stroke_width.style_src, SPStyleSrc::STYLE_PROP); + + auto four = cast(doc->getObjectById("four")); + ASSERT_TRUE(four != nullptr); + + EXPECT_EQ(four->style->fill.style_src, SPStyleSrc::STYLE_SHEET); + EXPECT_EQ(four->style->stroke.style_src, SPStyleSrc::STYLE_PROP); + EXPECT_EQ(four->style->opacity.style_src, SPStyleSrc::STYLE_SHEET); + EXPECT_EQ(four->style->stroke_width.style_src, SPStyleSrc::STYLE_PROP); +} + +/* + * Test the breaking up of the font property and recreation into separate properties. + */ +TEST_F(ObjectTest, StyleFont) { + ASSERT_TRUE(doc != nullptr); + ASSERT_TRUE(doc->getRoot() != nullptr); + + SPRoot *root = doc->getRoot(); + ASSERT_TRUE(root->getRepr() != nullptr); + ASSERT_TRUE(root->hasChildren()); + + auto five = cast(doc->getObjectById("five")); + ASSERT_TRUE(five != nullptr); + + // Font property is ALWAYS unset as it's converted into specific font css properties + EXPECT_EQ(five->style->font.get_value(), Glib::ustring("")); + EXPECT_EQ(five->style->font_size.get_value(), Glib::ustring("12px")); + EXPECT_EQ(five->style->font_weight.get_value(), Glib::ustring("bold")); + EXPECT_EQ(five->style->font_style.get_value(), Glib::ustring("italic")); + EXPECT_EQ(five->style->font_family.get_value(), Glib::ustring("arial, sans-serif")); +} + +/* + * Test the consumption of font dependent lengths in SPILength, e.g. EM, EX and % units + */ +TEST_F(ObjectTest, StyleFontSizes) { + ASSERT_TRUE(doc != nullptr); + ASSERT_TRUE(doc->getRoot() != nullptr); + + SPRoot *root = doc->getRoot(); + ASSERT_TRUE(root->getRepr() != nullptr); + ASSERT_TRUE(root->hasChildren()); + + auto six = cast(doc->getObjectById("six")); + ASSERT_TRUE(six != nullptr); + + EXPECT_EQ(six->style->stroke_width.get_value(), Glib::ustring("1em")); + EXPECT_EQ(six->style->stroke_width.computed, 14); + + auto seven = cast(doc->getObjectById("seven")); + ASSERT_TRUE(seven != nullptr); + + EXPECT_EQ(seven->style->stroke_width.get_value(), Glib::ustring("1ex")); + EXPECT_EQ(seven->style->stroke_width.computed, 7); + + auto eight = cast(doc->getObjectById("eight")); + ASSERT_TRUE(eight != nullptr); + + EXPECT_EQ(eight->style->stroke_width.get_value(), Glib::ustring("50%")); + + // stroke-width in percent is relative to viewport size, which is 300x150 in this example. + // 50% is 118.59 == ((300^2 + 150^2) / 2)^0.5 * 0.5 + EXPECT_FLOAT_EQ(eight->style->stroke_width.computed, 118.58541); +} diff --git a/testfiles/src/object-test.cpp b/testfiles/src/object-test.cpp new file mode 100644 index 0000000..a2e69e5 --- /dev/null +++ b/testfiles/src/object-test.cpp @@ -0,0 +1,204 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Unit tests migrated from cxxtest + * + * Authors: + * Adrian Boguszewski + * + * Copyright (C) 2018 Authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include +#include +#include +#include + +using namespace Inkscape; +using namespace Inkscape::XML; + +class ObjectTest: public DocPerCaseTest { +public: + ObjectTest() { + // Sample document + // svg:svg + // svg:defs + // svg:path + // svg:linearGradient + // svg:stop + // svg:filter + // svg:feGaussianBlur (feel free to implement for other filters) + // svg:clipPath + // svg:rect + // svg:g + // svg:use + // svg:circle + // svg:ellipse + // svg:text + // svg:polygon + // svg:polyline + // svg:image + // svg:line + char const *docString = R"A( + + + SVG test + + + + + + + + + + + + + + + + + + + TEST + + + + + + + )A"; + doc.reset(SPDocument::createNewDocFromMem(docString, static_cast(strlen(docString)), false)); + } + + ~ObjectTest() override = default; + + std::unique_ptr doc; +}; + +TEST_F(ObjectTest, Clones) { + ASSERT_TRUE(doc != nullptr); + ASSERT_TRUE(doc->getRoot() != nullptr); + + SPRoot *root = doc->getRoot(); + ASSERT_TRUE(root->getRepr() != nullptr); + ASSERT_TRUE(root->hasChildren()); + + auto path = cast(doc->getObjectById("P")); + ASSERT_TRUE(path != nullptr); + + Node *node = path->getRepr(); + ASSERT_TRUE(node != nullptr); + + Document *xml_doc = node->document(); + ASSERT_TRUE(xml_doc != nullptr); + + Node *parent = node->parent(); + ASSERT_TRUE(parent != nullptr); + + const size_t num_clones = 1000; + std::string href = std::string("#") + std::string(path->getId()); + std::vector clones(num_clones, nullptr); + + // Create num_clones clones of this path and stick them in the document + for (size_t i = 0; i < num_clones; ++i) { + Node *clone = xml_doc->createElement("svg:use"); + Inkscape::GC::release(clone); + clone->setAttribute("xlink:href", href); + parent->addChild(clone, node); + clones[i] = clone; + } + + // Remove those clones + for (size_t i = 0; i < num_clones; ++i) { + parent->removeChild(clones[i]); + } +} + +TEST_F(ObjectTest, Grouping) { + ASSERT_TRUE(doc != nullptr); + ASSERT_TRUE(doc->getRoot() != nullptr); + + SPRoot *root = doc->getRoot(); + ASSERT_TRUE(root->getRepr() != nullptr); + ASSERT_TRUE(root->hasChildren()); + + auto group = cast(doc->getObjectById("G")); + + ASSERT_TRUE(group != nullptr); + + Node *node = group->getRepr(); + ASSERT_TRUE(node != nullptr); + + Document *xml_doc = node->document(); + ASSERT_TRUE(xml_doc != nullptr); + + const size_t num_elements = 1000; + + Node *new_group = xml_doc->createElement("svg:g"); + Inkscape::GC::release(new_group); + node->addChild(new_group, nullptr); + + std::vector elements(num_elements, nullptr); + + for (size_t i = 0; i < num_elements; ++i) { + Node *circle = xml_doc->createElement("svg:circle"); + Inkscape::GC::release(circle); + circle->setAttribute("cx", "2048"); + circle->setAttribute("cy", "1024"); + circle->setAttribute("r", "1.5"); + new_group->addChild(circle, nullptr); + elements[i] = circle; + } + + auto n_group = cast(group->get_child_by_repr(new_group)); + ASSERT_TRUE(n_group != nullptr); + + std::vector ch; + sp_item_group_ungroup(n_group, ch); + + // Remove those elements + for (size_t i = 0; i < num_elements; ++i) { + elements[i]->parent()->removeChild(elements[i]); + } + +} + +TEST_F(ObjectTest, Objects) { + ASSERT_TRUE(doc != nullptr); + ASSERT_TRUE(doc->getRoot() != nullptr); + + SPRoot *root = doc->getRoot(); + ASSERT_TRUE(root->getRepr() != nullptr); + ASSERT_TRUE(root->hasChildren()); + + auto path = cast(doc->getObjectById("P")); + ASSERT_TRUE(path != nullptr); + + // Test parent behavior + SPObject *child = root->firstChild(); + ASSERT_TRUE(child != nullptr); + + EXPECT_EQ(root, child->parent); + EXPECT_EQ(doc.get(), child->document); + EXPECT_TRUE(root->isAncestorOf(child)); + + // Test list behavior + SPObject *next = child->getNext(); + SPObject *prev = next; + EXPECT_EQ(child, next->getPrev()); + + prev = next; + next = next->getNext(); + while (next != nullptr) { + // Walk the list + EXPECT_EQ(prev, next->getPrev()); + prev = next; + next = next->getNext(); + } + + // Test hrefcount + EXPECT_TRUE(path->isReferenced()); +} diff --git a/testfiles/src/oklab-color-test.cpp b/testfiles/src/oklab-color-test.cpp new file mode 100644 index 0000000..f4cc949 --- /dev/null +++ b/testfiles/src/oklab-color-test.cpp @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** @file Tests for the OKLab/OKLch color space backend. + */ +/* + * Authors: + * RafaƂ Siejakowski + * + * Copyright (C) 2022 Authors + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include +#include + +#include "color.h" +#include "oklab.h" + +unsigned constexpr L=0, A=1, B=2; +double constexpr EPS = 1e-7; + +inline Oklab::Triplet random_triplet() +{ + return { g_random_double_range(0.0, 1.0), + g_random_double_range(0.0, 1.0), + g_random_double_range(0.0, 1.0) }; +} + +/** Test converting black and white to OKLab. */ +TEST(OklabColorTest, BlackWhite) +{ + using namespace Oklab; + + auto const black = linear_rgb_to_oklab({0, 0, 0}); + EXPECT_NEAR(black[L], 0.0, EPS); + EXPECT_NEAR(black[A], 0.0, EPS); + EXPECT_NEAR(black[B], 0.0, EPS); + + auto const white = linear_rgb_to_oklab({1.0, 1.0, 1.0}); + EXPECT_NEAR(white[L], 1.0, EPS); + EXPECT_NEAR(white[A], 0.0, EPS); + EXPECT_NEAR(white[B], 0.0, EPS); +} + +/** Test linear RGB -> OKLab -> linear RGB roundtrip. */ +TEST(OKlabColorTest, RGBRoundrtip) +{ + using namespace Oklab; + g_random_set_seed(13375336); // We always seed for tests' repeatability + + for (unsigned i = 0; i < 10'000; i++) { + Triplet rgb = random_triplet(); + auto const roundtrip = oklab_to_linear_rgb(linear_rgb_to_oklab(rgb)); + for (size_t i : {0, 1, 2}) { + EXPECT_NEAR(roundtrip[i], rgb[i], EPS); + } + } +} + +/** Test OKLab -> linear RGB -> OKLab roundtrip. */ +TEST(OKlabColorTest, OklabRoundrtip) +{ + using namespace Oklab; + g_random_set_seed(0xCAFECAFE); + + for (unsigned i = 0; i < 10'000; i++) { + Triplet lab = linear_rgb_to_oklab(random_triplet()); + auto const roundtrip = linear_rgb_to_oklab(oklab_to_linear_rgb(lab)); + for (size_t i : {0, 1, 2}) { + EXPECT_NEAR(roundtrip[i], lab[i], EPS); + } + } +} + +/** Test OKLab -> OKLch -> OKLab roundtrip. */ +TEST(OKlabColorTest, PolarRectRoundrtip) +{ + using namespace Oklab; + g_random_set_seed(0xB747A380); + + for (unsigned i = 0; i < 10'000; i++) { + Triplet lab = linear_rgb_to_oklab(random_triplet()); + auto const roundtrip = oklch_to_oklab(oklab_to_oklch(lab)); + for (size_t i : {1, 2}) { // No point testing [0] since L == L + EXPECT_NEAR(roundtrip[i], lab[i], EPS); + } + } +} + +/** Test OKLch -> OKLab -> OKLch roundtrip. */ +TEST(OKlabColorTest, RectPolarRoundrtip) +{ + using namespace Oklab; + g_random_set_seed(0xFA18B52); + + for (unsigned i = 0; i < 10'000; i++) { + Triplet lch = oklab_to_oklch(linear_rgb_to_oklab(random_triplet())); + auto const roundtrip = oklab_to_oklch(oklch_to_oklab(lch)); + for (size_t i : {1, 2}) { // No point testing [0] + EXPECT_NEAR(roundtrip[i], lch[i], EPS); + } + } +} + +/** Test maximum chroma calculations. */ +TEST(OKlabColorTest, Saturate) +{ + using namespace Oklab; + g_random_set_seed(0x987654); + + /** Test whether a number lies near to the endpoint of the unit interval. */ + auto const near_end = [](double x) -> bool { + return x > 0.999 || x < 0.0001; + }; + + for (unsigned i = 0; i < 10'000; i++) { + // Get a random l, h pair and compute the maximum chroma. + auto [l, _, h] = oklab_to_oklch(linear_rgb_to_oklab(random_triplet())); + auto const chromax = max_chroma(l, h); + + // Try maximally saturating the color and verifying that after converting + // the result to RGB we end up hitting the boundary of the sRGB gamut. + auto [r, g, b] = oklab_to_linear_rgb(oklch_to_oklab({l, chromax, h})); + EXPECT_TRUE(near_end(r) || near_end(g) || near_end(b)); + } +} + +/** Test OKHSL -> OKLab -> OKHSL conversion roundtrip. */ +TEST(OKlabColorTest, HSLabRoundtrip) +{ + using namespace Oklab; + g_random_set_seed(908070); + + for (unsigned i = 0; i < 10'000; i++) { + auto const hsl = random_triplet(); + if (hsl[1] < 0.001) { + // Grayscale colors don't have unique hues, + // so we skip them (mapping is not bijective). + continue; + } + auto const roundtrip = oklab_to_okhsl(okhsl_to_oklab(hsl)); + for (size_t i : {0, 1, 2}) { + EXPECT_NEAR(roundtrip[i], hsl[i], EPS); + } + } +} + +/** Test OKLab -> OKHSL -> OKLab conversion roundtrip. */ +TEST(OKlabColorTest, LabHSLRoundtrip) +{ + using namespace Oklab; + g_random_set_seed(5043071); + + for (unsigned i = 0; i < 10'000; i++) { + auto const lab = linear_rgb_to_oklab(random_triplet()); + auto const roundtrip = okhsl_to_oklab(oklab_to_okhsl(lab)); + for (size_t i : {0, 1, 2}) { + EXPECT_NEAR(roundtrip[i], lab[i], EPS); + } + } +} + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : \ No newline at end of file diff --git a/testfiles/src/path-boolop-test.cpp b/testfiles/src/path-boolop-test.cpp new file mode 100644 index 0000000..cc71856 --- /dev/null +++ b/testfiles/src/path-boolop-test.cpp @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include +#include +#include +#include <2geom/svg-path-writer.h> + +class PathBoolopTest : public ::testing::Test +{ + public: + std::string rectangle_bigger = "M 0,0 L 0,2 L 2,2 L 2,0 z"; + std::string rectangle_smaller = "M 0.5,0.5 L 0.5,1.5 L 1.5,1.5 L 1.5,0.5 z"; + std::string rectangle_outside = "M 0,1.5 L 0.5,1.5 L 0.5,2.5 L 0,2.5 z"; + std::string rectangle_outside_union = "M 0,0 L 0,1.5 L 0,2 L 0,2.5 L 0.5,2.5 L 0.5,2 L 2,2 L 2,0 L 0,0 z"; + Geom::PathVector pvRectangleBigger; + Geom::PathVector pvRectangleSmaller; + Geom::PathVector pvRectangleOutside; + Geom::PathVector pvTargetUnion; + Geom::PathVector pvEmpty; + PathBoolopTest() { + pvRectangleBigger = sp_svg_read_pathv(rectangle_bigger.c_str()); + pvRectangleSmaller = sp_svg_read_pathv(rectangle_smaller.c_str()); + pvRectangleOutside = sp_svg_read_pathv(rectangle_outside.c_str()); + pvTargetUnion = sp_svg_read_pathv(rectangle_outside_union.c_str()); + pvEmpty = sp_svg_read_pathv(""); + } + void comparePaths(Geom::PathVector result, Geom::PathVector target){ + Geom::SVGPathWriter wr; + wr.feed(result); + std::string resultD = wr.str(); + wr.clear(); + wr.feed(target); + std::string targetD = wr.str(); + EXPECT_EQ(resultD, targetD); + EXPECT_EQ(result, target); + } +}; + +TEST_F(PathBoolopTest, UnionOutside){ + // test that the union of two objects where one is outside the other results in a new larger shape + Geom::PathVector pvRectangleUnion = sp_pathvector_boolop(pvRectangleBigger, pvRectangleOutside, bool_op_union, fill_oddEven, fill_oddEven); + comparePaths(pvRectangleUnion, pvTargetUnion); +} + +TEST_F(PathBoolopTest, UnionOutsideSwap){ + // test that the union of two objects where one is outside the other results in a new larger shape, even when the order is reversed + Geom::PathVector pvRectangleUnion = sp_pathvector_boolop(pvRectangleOutside, pvRectangleBigger, bool_op_union, fill_oddEven, fill_oddEven); + comparePaths(pvRectangleUnion, pvTargetUnion); +} + +TEST_F(PathBoolopTest, UnionInside){ + // test that the union of two objects where one is completely inside the other is the larger shape + Geom::PathVector pvRectangleUnion = sp_pathvector_boolop(pvRectangleBigger, pvRectangleSmaller, bool_op_union, fill_oddEven, fill_oddEven); + comparePaths(pvRectangleUnion, pvRectangleBigger); +} + +TEST_F(PathBoolopTest, UnionInsideSwap){ + // test that the union of two objects where one is completely inside the other is the larger shape, even when the order is swapped + Geom::PathVector pvRectangleUnion = sp_pathvector_boolop(pvRectangleSmaller, pvRectangleBigger, bool_op_union, fill_oddEven, fill_oddEven); + comparePaths(pvRectangleUnion, pvRectangleBigger); +} + +TEST_F(PathBoolopTest, IntersectionInside){ + // test that the intersection of two objects where one is completely inside the other is the smaller shape + Geom::PathVector pvRectangleIntersection = sp_pathvector_boolop(pvRectangleBigger, pvRectangleSmaller, bool_op_inters, fill_oddEven, fill_oddEven); + comparePaths(pvRectangleIntersection, pvRectangleSmaller); +} + +TEST_F(PathBoolopTest, DifferenceInside){ + // test that the difference of two objects where one is completely inside the other is an empty path + Geom::PathVector pvRectangleDifference = sp_pathvector_boolop(pvRectangleBigger, pvRectangleSmaller, bool_op_diff, fill_oddEven, fill_oddEven); + comparePaths(pvRectangleDifference, pvEmpty); +} + +TEST_F(PathBoolopTest, DifferenceOutside){ + // test that the difference of two objects where one is completely outside the other is multiple shapes + Geom::PathVector pvRectangleDifference = sp_pathvector_boolop(pvRectangleSmaller, pvRectangleBigger, bool_op_diff, fill_oddEven, fill_oddEven); + Geom::PathVector pvBothPaths = pvRectangleBigger; + + for(Geom::Path _path : pvRectangleSmaller){ + pvBothPaths.push_back(_path); + } + + comparePaths(pvRectangleDifference, pvBothPaths); +} + +// \ No newline at end of file diff --git a/testfiles/src/path-reverse-lpe-test.cpp b/testfiles/src/path-reverse-lpe-test.cpp new file mode 100644 index 0000000..6e5b21e --- /dev/null +++ b/testfiles/src/path-reverse-lpe-test.cpp @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** @file + * Test for https://gitlab.com/inkscape/inkscape/-/issues/3393 + *//* + * + * Authors: + * Thomas Holder + * + * Copyright (C) 2022 Authors + * + * Released under GNU GPL version 2 or later, read the file 'COPYING' for more information + */ + +#include +#include +#include +#include + +using namespace Inkscape; + +static char const *const docString = R"""( + + + + + + +)"""; + +TEST_F(DocPerCaseTest, PathReverse) +{ + auto doc = std::unique_ptr(SPDocument::createNewDocFromMem(docString, strlen(docString), false)); + doc->ensureUpToDate(); + + auto path1 = cast(doc->getObjectById("path1")); + auto oset = ObjectSet(doc.get()); + oset.add(path1); + + ASSERT_EQ(*path1->curve()->first_point(), Geom::Point(5, 5)); + + oset.pathReverse(); + + ASSERT_EQ(*path1->curve()->first_point(), Geom::Point(15, 15)); +} diff --git a/testfiles/src/rebase-hrefs-test.cpp b/testfiles/src/rebase-hrefs-test.cpp new file mode 100644 index 0000000..e1c91fb --- /dev/null +++ b/testfiles/src/rebase-hrefs-test.cpp @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** + * @file + * Test rebasing URI attributes + */ +/* + * Authors: + * Thomas Holder + * + * Copyright (C) 2020 Authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include "xml/rebase-hrefs.h" + +#include +#include + +#include "object/sp-object.h" + +using namespace Inkscape::XML; + +#ifdef _WIN32 +#define BASE_DIR_DIFFERENT_ROOT "D:\\foo\\bar" +#define BASE_DIR "C:\\foo\\bar" +#define BASE_URL "file:///C:/foo/bar" +#else +#define BASE_DIR_DIFFERENT_ROOT "/different/root" +#define BASE_DIR "/foo/bar" +#define BASE_URL "file://" BASE_DIR +#endif + +static char const *const docString = R"""( + + + + + + + + + + + + + + + + + + + + +)"""; + +class ObjectTest : public DocPerCaseTest +{ +public: + std::unique_ptr doc; + + ObjectTest() { doc.reset(SPDocument::createNewDocFromMem(docString, strlen(docString), false)); } + + void assert_nonfile_unchanged() const + { + ASSERT_STREQ(doc->getObjectById("img03")->getAttribute("xlink:href"), "http://host/a.png"); + ASSERT_STREQ(doc->getObjectById("img04")->getAttribute("xlink:href"), "data:text/plain,xxx"); + + ASSERT_STREQ(doc->getObjectById("img05")->getAttribute("xlink:href"), ""); + ASSERT_STREQ(doc->getObjectById("img06")->getAttribute("xlink:href"), "#fragment"); + ASSERT_STREQ(doc->getObjectById("img07")->getAttribute("xlink:href"), "?query"); + ASSERT_STREQ(doc->getObjectById("img08")->getAttribute("xlink:href"), "/absolute/path"); + ASSERT_STREQ(doc->getObjectById("img09")->getAttribute("xlink:href"), "//network/path"); + } +}; + +TEST_F(ObjectTest, RebaseHrefs) +{ + rebase_hrefs(doc.get(), BASE_DIR G_DIR_SEPARATOR_S "c", false); + assert_nonfile_unchanged(); + ASSERT_STREQ(doc->getObjectById("img01")->getAttribute("xlink:href"), "../a.png"); + ASSERT_STREQ(doc->getObjectById("img02")->getAttribute("xlink:href"), "b/a.png"); + + // no base + rebase_hrefs(doc.get(), nullptr, false); + assert_nonfile_unchanged(); + ASSERT_STREQ(doc->getObjectById("img01")->getAttribute("xlink:href"), BASE_URL "/a.png"); + ASSERT_STREQ(doc->getObjectById("img02")->getAttribute("xlink:href"), BASE_URL "/c/b/a.png"); + + rebase_hrefs(doc.get(), BASE_DIR, false); + assert_nonfile_unchanged(); + ASSERT_STREQ(doc->getObjectById("img01")->getAttribute("xlink:href"), "a.png"); + ASSERT_STREQ(doc->getObjectById("img02")->getAttribute("xlink:href"), "c/b/a.png"); + + // base with different root + rebase_hrefs(doc.get(), BASE_DIR_DIFFERENT_ROOT, false); + assert_nonfile_unchanged(); + ASSERT_STREQ(doc->getObjectById("img01")->getAttribute("xlink:href"), BASE_URL "/a.png"); + ASSERT_STREQ(doc->getObjectById("img02")->getAttribute("xlink:href"), BASE_URL "/c/b/a.png"); +} + +static std::map rebase_attrs_test_helper(SPDocument *doc, char const *id, + char const *old_base, char const *new_base) +{ + std::map attributemap; + auto attributes = rebase_href_attrs(old_base, new_base, doc->getObjectById(id)->getRepr()->attributeList()); + for (const auto &item : attributes) { + attributemap[g_quark_to_string(item.key)] = item.value.pointer(); + } + return attributemap; +} + +TEST_F(ObjectTest, RebaseHrefAttrs) +{ + std::map amap; + + amap = rebase_attrs_test_helper(doc.get(), "img01", BASE_DIR, BASE_DIR G_DIR_SEPARATOR_S "c"); + ASSERT_STREQ(amap["xlink:href"].c_str(), "../a.png"); + amap = rebase_attrs_test_helper(doc.get(), "img02", BASE_DIR, BASE_DIR G_DIR_SEPARATOR_S "c"); + ASSERT_STREQ(amap["xlink:href"].c_str(), "b/a.png"); + amap = rebase_attrs_test_helper(doc.get(), "img06", BASE_DIR, BASE_DIR G_DIR_SEPARATOR_S "c"); + ASSERT_STREQ(amap["xlink:href"].c_str(), "#fragment"); + amap = rebase_attrs_test_helper(doc.get(), "img10", BASE_DIR, BASE_DIR G_DIR_SEPARATOR_S "c"); + ASSERT_STREQ(amap["xlink:href"].c_str(), "../b/a.png"); + + amap = rebase_attrs_test_helper(doc.get(), "a01", BASE_DIR, BASE_DIR G_DIR_SEPARATOR_S "c"); + ASSERT_STREQ(amap["xlink:href"].c_str(), "../other.svg"); + amap = rebase_attrs_test_helper(doc.get(), "a02", BASE_DIR, BASE_DIR G_DIR_SEPARATOR_S "c"); + ASSERT_STREQ(amap["xlink:href"].c_str(), "http://host/other.svg"); + + amap = rebase_attrs_test_helper(doc.get(), "img01", BASE_DIR, BASE_DIR_DIFFERENT_ROOT); + ASSERT_STREQ(amap["xlink:href"].c_str(), BASE_URL "/a.png"); +} + +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/testfiles/src/sp-glyph-kerning-test.cpp b/testfiles/src/sp-glyph-kerning-test.cpp new file mode 100644 index 0000000..2c737ee --- /dev/null +++ b/testfiles/src/sp-glyph-kerning-test.cpp @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** @file + * SPGlyphKerning test + *//* + * + * Authors: + * Cosmin Dancu + * + * Copyright (C) 2020 Authors + * + * Released under GNU GPL version 2 or later, read the file 'COPYING' for more information + */ + +#include +#include + +TEST(SPGlyphKerningTest, EmptyGlyphNamesDoNotContainAnything) { + GlyphNames empty_glyph_names(nullptr); + ASSERT_FALSE(empty_glyph_names.contains("foo")); +} + +TEST(SPGlyphKerningTest, GlyphNamesContainEachName) { + GlyphNames glyph_names("name1 name2"); + ASSERT_TRUE(glyph_names.contains("name1")); + ASSERT_TRUE(glyph_names.contains("name2")); +} diff --git a/testfiles/src/sp-gradient-test.cpp b/testfiles/src/sp-gradient-test.cpp new file mode 100644 index 0000000..a4dd115 --- /dev/null +++ b/testfiles/src/sp-gradient-test.cpp @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Unit tests migrated from cxxtest + * + * Authors: + * Adrian Boguszewski + * + * Copyright (C) 2018 Authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include +#include +#include +#include +#include <2geom/transforms.h> +#include +#include +#include + +using namespace Inkscape; +using namespace Inkscape::XML; + +class SPGradientTest: public DocPerCaseTest { +public: + SPGradientTest() { + DocPerCaseTest::SetUpTestCase(); + gr = new SPGradient(); + } + + ~SPGradientTest() override { + delete gr; + DocPerCaseTest::TearDownTestCase(); + } + + SPGradient *gr; +}; + +TEST_F(SPGradientTest, Init) { + ASSERT_TRUE(gr != nullptr); + EXPECT_TRUE(gr->gradientTransform.isIdentity()); + EXPECT_TRUE(Geom::are_near(Geom::identity(), gr->gradientTransform)); +} + +TEST_F(SPGradientTest, SetGradientTransform) { + gr->document = _doc.get(); + + gr->setKeyValue(SPAttr::GRADIENTTRANSFORM, "translate(5, 8)"); + EXPECT_TRUE(Geom::are_near(Geom::Affine(Geom::Translate(5.0, 8.0)), gr->gradientTransform)); + + gr->setKeyValue(SPAttr::GRADIENTTRANSFORM, ""); + EXPECT_TRUE(Geom::are_near(Geom::identity(), gr->gradientTransform)); + + gr->setKeyValue(SPAttr::GRADIENTTRANSFORM, "rotate(90)"); + EXPECT_TRUE(Geom::are_near(Geom::Affine(Geom::Rotate::from_degrees(90.0)), gr->gradientTransform)); +} + +TEST_F(SPGradientTest, Write) { + gr->document = _doc.get(); + + gr->setKeyValue(SPAttr::GRADIENTTRANSFORM, "matrix(0, 1, -1, 0, 0, 0)"); + Document *xml_doc = _doc->getReprDoc(); + + ASSERT_TRUE(xml_doc != nullptr); + + Node *repr = xml_doc->createElement("svg:radialGradient"); + gr->updateRepr(xml_doc, repr, SP_OBJECT_WRITE_ALL); + + gchar const *tr = repr->attribute("gradientTransform"); + Geom::Affine svd; + bool const valid = sp_svg_transform_read(tr, &svd); + + EXPECT_TRUE(valid); + EXPECT_TRUE(Geom::are_near(Geom::Affine(Geom::Rotate::from_degrees(90.0)), svd)); +} + +TEST_F(SPGradientTest, GetG2dGetGs2dSetGs2) { + gr->document = _doc.get(); + + Geom::Affine grXform(2, 1, + 1, 3, + 4, 6); + gr->gradientTransform = grXform; + + Geom::Rect unit_rect(Geom::Point(0, 0), Geom::Point(1, 1)); + { + Geom::Affine g2d(gr->get_g2d_matrix(Geom::identity(), unit_rect)); + Geom::Affine gs2d(gr->get_gs2d_matrix(Geom::identity(), unit_rect)); + EXPECT_TRUE(Geom::are_near(Geom::identity(), g2d)); + EXPECT_TRUE(Geom::are_near(gs2d, gr->gradientTransform * g2d, 1e-12)); + + gr->set_gs2d_matrix(Geom::identity(), unit_rect, gs2d); + EXPECT_TRUE(Geom::are_near(gr->gradientTransform, grXform, 1e-12)); + } + + gr->gradientTransform = grXform; + Geom::Affine funny(2, 3, + 4, 5, + 6, 7); + { + Geom::Affine g2d(gr->get_g2d_matrix(funny, unit_rect)); + Geom::Affine gs2d(gr->get_gs2d_matrix(funny, unit_rect)); + EXPECT_TRUE(Geom::are_near(funny, g2d)); + EXPECT_TRUE(Geom::are_near(gs2d, gr->gradientTransform * g2d, 1e-12)); + + gr->set_gs2d_matrix(funny, unit_rect, gs2d); + EXPECT_TRUE(Geom::are_near(gr->gradientTransform, grXform, 1e-12)); + } + + gr->gradientTransform = grXform; + Geom::Rect larger_rect(Geom::Point(5, 6), Geom::Point(8, 10)); + { + Geom::Affine g2d(gr->get_g2d_matrix(funny, larger_rect)); + Geom::Affine gs2d(gr->get_gs2d_matrix(funny, larger_rect)); + EXPECT_TRUE(Geom::are_near(Geom::Affine(3, 0, + 0, 4, + 5, 6) * funny, g2d )); + EXPECT_TRUE(Geom::are_near(gs2d, gr->gradientTransform * g2d, 1e-12)); + + gr->set_gs2d_matrix(funny, larger_rect, gs2d); + EXPECT_TRUE(Geom::are_near(gr->gradientTransform, grXform, 1e-12)); + + gr->setKeyValue( SPAttr::GRADIENTUNITS, "userSpaceOnUse"); + Geom::Affine user_g2d(gr->get_g2d_matrix(funny, larger_rect)); + Geom::Affine user_gs2d(gr->get_gs2d_matrix(funny, larger_rect)); + EXPECT_TRUE(Geom::are_near(funny, user_g2d)); + EXPECT_TRUE(Geom::are_near(user_gs2d, gr->gradientTransform * user_g2d, 1e-12)); + } +} diff --git a/testfiles/src/sp-item-group-test.cpp b/testfiles/src/sp-item-group-test.cpp new file mode 100644 index 0000000..aefd7d3 --- /dev/null +++ b/testfiles/src/sp-item-group-test.cpp @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** @file + * SPGroup test + *//* + * Authors: see git history + * + * Copyright (C) 2020 Authors + * + * Released under GNU GPL version 2 or later, read the file 'COPYING' for more information + */ + +#include +#include +#include +#include +#include + +using namespace Inkscape; +using namespace Inkscape::LivePathEffect; + +class SPGroupTest : public ::testing::Test { + protected: + void SetUp() override + { + // setup hidden dependency + Application::create(false); + } +}; + +TEST_F(SPGroupTest, applyingPowerClipEffectToGroupWithoutClipIsIgnored) +{ + std::string svg("\ +\ + \ + \ + \ + \ +"); + + SPDocument *doc = SPDocument::createNewDocFromMem(svg.c_str(), svg.size(), true); + + auto group = cast(doc->getObjectById("group1")); + Effect::createAndApply(POWERCLIP, doc, group); + + ASSERT_FALSE(group->hasPathEffect()); +} diff --git a/testfiles/src/sp-object-tags-test.cpp b/testfiles/src/sp-object-tags-test.cpp new file mode 100644 index 0000000..89db015 --- /dev/null +++ b/testfiles/src/sp-object-tags-test.cpp @@ -0,0 +1,235 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +#include +#include +#include + +#include "object/box3d.h" +#include "object/box3d-side.h" +#include "object/color-profile.h" +#include "object/persp3d.h" +#include "object/sp-anchor.h" +#include "object/sp-clippath.h" +#include "object/sp-defs.h" +#include "object/sp-desc.h" +#include "object/sp-ellipse.h" +#include "object/sp-filter.h" +#include "object/sp-flowdiv.h" +#include "object/sp-flowregion.h" +#include "object/sp-flowtext.h" +#include "object/sp-font.h" +#include "object/sp-font-face.h" +#include "object/sp-glyph.h" +#include "object/sp-glyph-kerning.h" +#include "object/sp-grid.h" +#include "object/sp-guide.h" +#include "object/sp-hatch.h" +#include "object/sp-hatch-path.h" +#include "object/sp-image.h" +#include "object/sp-line.h" +#include "object/sp-linear-gradient.h" +#include "object/sp-marker.h" +#include "object/sp-mask.h" +#include "object/sp-mesh-gradient.h" +#include "object/sp-mesh-patch.h" +#include "object/sp-mesh-row.h" +#include "object/sp-metadata.h" +#include "object/sp-missing-glyph.h" +#include "object/sp-namedview.h" +#include "object/sp-offset.h" +#include "object/sp-page.h" +#include "object/sp-path.h" +#include "object/sp-pattern.h" +#include "object/sp-polyline.h" +#include "object/sp-radial-gradient.h" +#include "object/sp-rect.h" +#include "object/sp-root.h" +#include "object/sp-script.h" +#include "object/sp-solid-color.h" +#include "object/sp-spiral.h" +#include "object/sp-star.h" +#include "object/sp-stop.h" +#include "object/sp-string.h" +#include "object/sp-style-elem.h" +#include "object/sp-switch.h" +#include "object/sp-symbol.h" +#include "object/sp-tag.h" +#include "object/sp-tag-use.h" +#include "object/sp-text.h" +#include "object/sp-textpath.h" +#include "object/sp-title.h" +#include "object/sp-tref.h" +#include "object/sp-tspan.h" +#include "object/sp-use.h" +#include "live_effects/lpeobject.h" +#include "object/filters/blend.h" +#include "object/filters/colormatrix.h" +#include "object/filters/componenttransfer.h" +#include "object/filters/componenttransfer-funcnode.h" +#include "object/filters/composite.h" +#include "object/filters/convolvematrix.h" +#include "object/filters/diffuselighting.h" +#include "object/filters/displacementmap.h" +#include "object/filters/distantlight.h" +#include "object/filters/flood.h" +#include "object/filters/gaussian-blur.h" +#include "object/filters/image.h" +#include "object/filters/merge.h" +#include "object/filters/mergenode.h" +#include "object/filters/morphology.h" +#include "object/filters/offset.h" +#include "object/filters/pointlight.h" +#include "object/filters/specularlighting.h" +#include "object/filters/spotlight.h" +#include "object/filters/tile.h" +#include "object/filters/turbulence.h" + +namespace { + +// Error reporting function, because asserts can only be used inside test body. +using F = std::function; + +// Ensure tree structure is consistent with actual class hierarchy. +template +void test_real(F const &f) +{ + constexpr bool b1 = std::is_base_of_v; + constexpr bool b2 = first_tag <= tag_of && tag_of <= last_tag; + + if constexpr (b1 != b2) { + f(typeid(A).name(), typeid(B).name(), b1, b2); + } +} + +template +void test_dispatcher2(F const &f) +{ + test_real(f); + test_real(f); +} + +template +void test_dispatcher(F const &f) +{ + test_dispatcher2(f); + if constexpr (sizeof...(T) >= 1) { + test_dispatcher(f); + } +} + +// Calls test_real for all distinct pairs of types A, B in T. +template +void test(F const &f) +{ + if constexpr (sizeof...(T) >= 1) { + test_dispatcher(f); + } + if constexpr (sizeof...(T) >= 2) { + test(f); + } +} + +} // namespace + +TEST(SPObjectTagsTest, compare_dynamic_cast) +{ + test< + SPObject, + Inkscape::ColorProfile, + LivePathEffectObject, + Persp3D, + SPDefs, + SPDesc, + SPFeDistantLight, + SPFeFuncNode, + SPFeMergeNode, + SPFePointLight, + SPFeSpotLight, + SPFilter, + SPFilterPrimitive, + SPFeBlend, + SPFeColorMatrix, + SPFeComponentTransfer, + SPFeComposite, + SPFeConvolveMatrix, + SPFeDiffuseLighting, + SPFeDisplacementMap, + SPFeFlood, + SPFeImage, + SPFeMerge, + SPFeMorphology, + SPFeOffset, + SPFeSpecularLighting, + SPFeTile, + SPFeTurbulence, + SPGaussianBlur, + SPFlowline, + SPFlowregionbreak, + SPFont, + SPFontFace, + SPGlyph, + SPGlyphKerning, + SPHkern, + SPVkern, + SPGrid, + SPGuide, + SPHatchPath, + SPItem, + SPFlowdiv, + SPFlowpara, + SPFlowregion, + SPFlowregionExclude, + SPFlowtext, + SPFlowtspan, + SPImage, + SPLPEItem, + SPGroup, + SPAnchor, + SPBox3D, + SPMarker, + SPRoot, + SPSwitch, + SPSymbol, + SPShape, + SPGenericEllipse, + SPLine, + SPOffset, + SPPath, + SPPolyLine, + SPPolygon, + Box3DSide, + SPRect, + SPSpiral, + SPStar, + SPTRef, + SPTSpan, + SPText, + SPTextPath, + SPUse, + SPMeshpatch, + SPMeshrow, + SPMetadata, + SPMissingGlyph, + SPObjectGroup, + SPClipPath, + SPMask, + SPNamedView, + SPPage, + SPPaintServer, + SPGradient, + SPLinearGradient, + SPMeshGradient, + SPRadialGradient, + SPHatch, + SPPattern, + SPSolidColor, + SPScript, + SPStop, + SPString, + SPStyleElem, + SPTag, + SPTagUse, + SPTitle + >([&] (char const *a, char const *b, bool b1, bool b2) { + ADD_FAILURE() << "For downcasting " << a << " -> " << b << ", got " << b2 << ", expected " << b1; + }); +} diff --git a/testfiles/src/sp-object-test.cpp b/testfiles/src/sp-object-test.cpp new file mode 100644 index 0000000..90a635d --- /dev/null +++ b/testfiles/src/sp-object-test.cpp @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Multiindex container for selection + * + * Authors: + * Adrian Boguszewski + * + * Copyright (C) 2016 Adrian Boguszewski + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ +#include +#include +#include +#include +#include +#include +#include + +using namespace Inkscape; +using namespace Inkscape::XML; + +class SPObjectTest: public DocPerCaseTest { +public: + SPObjectTest() { + auto *const _doc = this->_doc.get(); + a = new SPItem(); + b = new SPItem(); + c = new SPItem(); + d = new SPItem(); + e = new SPItem(); + auto sd = new SimpleDocument(); + auto et = new TextNode(Util::share_string("e"), sd); + auto dt = new TextNode(Util::share_string("d"), sd); + auto ct = new TextNode(Util::share_string("c"), sd); + auto bt = new TextNode(Util::share_string("b"), sd); + auto at = new TextNode(Util::share_string("a"), sd); + e->invoke_build(_doc, et, 0); + d->invoke_build(_doc, dt, 0); + c->invoke_build(_doc, ct, 0); + b->invoke_build(_doc, bt, 0); + a->invoke_build(_doc, at, 0); + } + ~SPObjectTest() override { + delete e; + delete d; + delete c; + delete b; + delete a; + } + SPObject* a; + SPObject* b; + SPObject* c; + SPObject* d; + SPObject* e; +}; + +TEST_F(SPObjectTest, Basics) { + a->attach(c, a->lastChild()); + a->attach(b, nullptr); + a->attach(d, c); + EXPECT_TRUE(a->hasChildren()); + EXPECT_EQ(b, a->firstChild()); + EXPECT_EQ(d, a->lastChild()); + auto children = a->childList(false); + EXPECT_EQ(3, children.size()); + EXPECT_EQ(b, children[0]); + EXPECT_EQ(c, children[1]); + EXPECT_EQ(d, children[2]); + a->attach(b, a->lastChild()); + EXPECT_EQ(3, a->children.size()); + a->reorder(b, b); + EXPECT_EQ(3, a->children.size()); + EXPECT_EQ(b, &a->children.front()); + EXPECT_EQ(d, &a->children.back()); + a->reorder(b, d); + EXPECT_EQ(3, a->children.size()); + EXPECT_EQ(c, &a->children.front()); + EXPECT_EQ(b, &a->children.back()); + a->reorder(d, nullptr); + EXPECT_EQ(3, a->children.size()); + EXPECT_EQ(d, &a->children.front()); + EXPECT_EQ(b, &a->children.back()); + a->reorder(c, b); + EXPECT_EQ(3, a->children.size()); + EXPECT_EQ(d, &a->children.front()); + EXPECT_EQ(c, &a->children.back()); + a->detach(b); + EXPECT_EQ(c, a->lastChild()); + children = a->childList(false); + EXPECT_EQ(2, children.size()); + EXPECT_EQ(d, children[0]); + EXPECT_EQ(c, children[1]); + a->detach(b); + EXPECT_EQ(2, a->childList(false).size()); + a->releaseReferences(); + EXPECT_FALSE(a->hasChildren()); + EXPECT_EQ(nullptr, a->firstChild()); + EXPECT_EQ(nullptr, a->lastChild()); +} + +TEST_F(SPObjectTest, Advanced) { + a->attach(b, a->lastChild()); + a->attach(c, a->lastChild()); + a->attach(d, a->lastChild()); + a->attach(e, a->lastChild()); + EXPECT_EQ(e, a->get_child_by_repr(e->getRepr())); + EXPECT_EQ(c, a->get_child_by_repr(c->getRepr())); + EXPECT_EQ(d, e->getPrev()); + EXPECT_EQ(c, d->getPrev()); + EXPECT_EQ(b, c->getPrev()); + EXPECT_EQ(nullptr, b->getPrev()); + EXPECT_EQ(nullptr, e->getNext()); + EXPECT_EQ(e, d->getNext()); + EXPECT_EQ(d, c->getNext()); + EXPECT_EQ(c, b->getNext()); + std::vector tmp = {b, c, d, e}; + int index = 0; + for(auto& child: a->children) { + EXPECT_EQ(tmp[index++], &child); + } +} diff --git a/testfiles/src/stream-test.cpp b/testfiles/src/stream-test.cpp new file mode 100644 index 0000000..f6ddd53 --- /dev/null +++ b/testfiles/src/stream-test.cpp @@ -0,0 +1,161 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** @file + * Stream IO tests + *//* + * Authors: see git history + * + * Copyright (C) 2015-2023 Authors + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include +#include +#include + +#include "io/stream/gzipstream.h" +#include "io/stream/inkscapestream.h" +#include "io/stream/stringstream.h" +#include "io/stream/uristream.h" +#include "io/stream/xsltstream.h" + +// names and path storage for other tests +auto const xmlpath = INKSCAPE_TESTS_DIR "/data/crystalegg.xml"; +auto const xslpath = INKSCAPE_TESTS_DIR "/data/doc2html.xsl"; + +class MyFile +{ +protected: + std::string _filename; + std::string _mode; + +public: + MyFile(std::string filename, char const *mode = "rb") + : _filename(std::move(filename)) + , _mode(mode) + {} + + FILE *open(char const *mode) const { return std::fopen(_filename.c_str(), mode); } + + operator FILE *() const { return open(_mode.c_str()); } + + std::string getContents() const + { + std::string buf; + auto fp = std::unique_ptr(open("rb"), &std::fclose); + + if (!fp) { + ADD_FAILURE() << "failed to open " << _filename; + exit(1); + } + + for (int c; (c = std::fgetc(fp.get())) != EOF;) { + buf.push_back(c); + } + + return buf; + } +}; + +class MyOutFile : public MyFile +{ +public: + MyOutFile(std::string filename) + : MyFile("test_stream-out-" + filename, "wb") + {} + + ~MyOutFile() { std::remove(_filename.c_str()); } +}; + +TEST(StreamTest, FileStreamCopy) +{ + auto inFile = MyFile(xmlpath); + auto outFile = MyOutFile("streamtest.copy"); + { + auto ins = Inkscape::IO::FileInputStream(inFile); + auto outs = Inkscape::IO::FileOutputStream(outFile); + pipeStream(ins, outs); + } + ASSERT_EQ(inFile.getContents(), outFile.getContents()); +} + +TEST(StreamTest, OutputStreamWriter) +{ + Inkscape::IO::StdOutputStream outs; + Inkscape::IO::OutputStreamWriter writer(outs); + writer << "Hello, world! " << 123.45 << " times\n"; + writer.printf("There are %f quick brown foxes in %d states\n", 123.45, 88); +} + +TEST(StreamTest, StdWriter) +{ + Inkscape::IO::StdWriter writer; + writer << "Hello, world! " << 123.45 << " times\n"; + writer.printf("There are %f quick brown foxes in %d states\n", 123.45, 88); +} + +TEST(StreamTest, Xslt) +{ + // ######### XSLT Sheet ############ + auto xsltSheetFile = MyFile(xslpath); + auto xsltSheetIns = Inkscape::IO::FileInputStream(xsltSheetFile); + auto stylesheet = Inkscape::IO::XsltStyleSheet(xsltSheetIns); + xsltSheetIns.close(); + auto sourceFile = MyFile(xmlpath); + auto xmlIns = Inkscape::IO::FileInputStream(sourceFile); + + // ######### XSLT Input ############ + auto destFile = MyOutFile("test.html"); + auto xmlOuts = Inkscape::IO::FileOutputStream(destFile); + auto xsltIns = Inkscape::IO::XsltInputStream(xmlIns, stylesheet); + pipeStream(xsltIns, xmlOuts); + xsltIns.close(); + xmlOuts.close(); + + // ######### XSLT Output ############ + auto xmlIns2 = Inkscape::IO::FileInputStream(sourceFile); + auto destFile2 = MyOutFile("test2.html"); + auto xmlOuts2 = Inkscape::IO::FileOutputStream(destFile2); + auto xsltOuts = Inkscape::IO::XsltOutputStream(xmlOuts2, stylesheet); + pipeStream(xmlIns2, xsltOuts); + xmlIns2.close(); + xsltOuts.close(); + + auto htmlContent = destFile.getContents(); + ASSERT_NE(htmlContent.find(" +#include + +#include +#include +#include + +using namespace Inkscape; +using namespace Inkscape::XML; + +class ObjectTest: public DocPerCaseTest { +public: + ObjectTest() { + char const *docString = "\ +\ +\ +\ +"; + doc.reset(SPDocument::createNewDocFromMem(docString, static_cast(strlen(docString)), false)); + } + + ~ObjectTest() override = default; + + std::unique_ptr doc; +}; + +/* + * Test sp-style-element objects created in document. + */ +TEST_F(ObjectTest, StyleElems) { + ASSERT_TRUE(doc != nullptr); + ASSERT_TRUE(doc->getRoot() != nullptr); + + SPRoot *root = doc->getRoot(); + ASSERT_TRUE(root->getRepr() != nullptr); + + auto one = cast(doc->getObjectById("style01")); + ASSERT_TRUE(one != nullptr); + + for (auto &style : one->get_styles()) { + EXPECT_EQ(style->fill.get_value(), Glib::ustring("#ff0000")); + } + + auto two = cast(doc->getObjectById("style02")); + ASSERT_TRUE(one != nullptr); + + for (auto &style : two->get_styles()) { + EXPECT_EQ(style->fill.get_value(), Glib::ustring("#008000")); + } +} diff --git a/testfiles/src/style-internal-test.cpp b/testfiles/src/style-internal-test.cpp new file mode 100644 index 0000000..6312bb4 --- /dev/null +++ b/testfiles/src/style-internal-test.cpp @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** @file + * Tests for Style internal classes + *//* + * Authors: see git history + * + * Copyright (C) 2020 Authors + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ +#include +#include + +TEST(StyleInternalTest, testSPIDashArrayInequality) +{ + SPIDashArray array; + array.read("0 1 2 3"); + SPIDashArray subsetArray; + subsetArray.read("0 1"); + + ASSERT_FALSE(array == subsetArray); + ASSERT_FALSE(subsetArray == array); +} + +TEST(StyleInternalTest, testSPIDashArrayEquality) +{ + SPIDashArray anArray; + anArray.read("0 1 2 3"); + SPIDashArray sameArray; + sameArray.read("0 1 2 3"); + + ASSERT_TRUE(anArray == sameArray); + ASSERT_TRUE(sameArray == anArray); +} + +TEST(StyleInternalTest, testSPIDashArrayValidity) +{ + // valid dash arrays + SPIDashArray array10; + array10.read(""); + + SPIDashArray array11; + array11.read("0"); + + SPIDashArray array12; + array12.read("0 1e3"); + + // invalid dash arrayas + SPIDashArray array20; + array20.read("1-1"); + + SPIDashArray array21; + array21.read("10 10 -10"); + + SPIDashArray array22; + array22.read("-1"); + + SPIDashArray array23; + array23.read("0 -5e3"); + + + EXPECT_TRUE(array10.is_valid()); + EXPECT_TRUE(array11.is_valid()); + EXPECT_TRUE(array12.is_valid()); + + // SPIDashArray::read is geared towards happy path, so it may reject negative entries: + + // EXPECT_FALSE(array20.is_valid()); // cannot read "1-1" as numbers, so 0 + EXPECT_FALSE(array21.is_valid()); + // EXPECT_FALSE(array22.is_valid()); // lone negative number is deemed invalid and removed by 'read' + // EXPECT_FALSE(array23.is_valid()); // negative total: invalid and removed by 'read' +} + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/testfiles/src/style-test.cpp b/testfiles/src/style-test.cpp new file mode 100644 index 0000000..f0f427f --- /dev/null +++ b/testfiles/src/style-test.cpp @@ -0,0 +1,604 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/* + * Unit test for style properties. + * + * Author: + * Tavmjong Bah + * + * Copyright (C) 2017 Authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include +#include +#include + +#include "gtest/gtest.h" + +#include "style.h" + +namespace { + +class StyleRead { + +public: + StyleRead(std::string src, std::string dst, std::string uri) : + src(std::move(src)), dst(std::move(dst)), uri(std::move(uri)) + { + } + + StyleRead(std::string src, std::string dst) : + src(std::move(src)), dst(std::move(dst)), uri("") + { + } + + StyleRead(std::string const &src) : + src(src), dst(src), uri("") + { + } + + std::string src; + std::string dst; + std::string uri; + +}; + +std::vector getStyleData() +{ + StyleRead all_style_data[] = { + + // Paint ----------------------------------------------- + StyleRead("fill:none"), StyleRead("fill:currentColor"), StyleRead("fill:#ff00ff"), + StyleRead("fill:rgb(100%, 0%, 100%)", "fill:#ff00ff"), StyleRead("fill:rgb(255, 0, 255)", "fill:#ff00ff"), + + // TODO - fix this to preserve the string + // StyleRead("fill:url(#painter) rgb(100%, 0%, 100%)", + // "fill:url(#painter) #ff00ff", "#painter" ), + + // TODO - fix this to preserve the string + // StyleRead("fill:url(#painter) rgb(255, 0, 255)", + // "fill:url(#painter) #ff00ff", "#painter"), + + + StyleRead("fill:#ff00ff icc-color(colorChange, 0.1, 0.5, 0.1)"), + + // StyleRead("fill:url(#painter)", "", "#painter"), + // StyleRead("fill:url(#painter) none", "", "#painter"), + // StyleRead("fill:url(#painter) currentColor", "", "#painter"), + // StyleRead("fill:url(#painter) #ff00ff", "", "#painter"), + // StyleRead("fill:url(#painter) rgb(100%, 0%, 100%)", "", "#painter"), + // StyleRead("fill:url(#painter) rgb(255, 0, 255)", "", "#painter"), + + // StyleRead("fill:url(#painter) #ff00ff icc-color(colorChange, 0.1, 0.5, 0.1)", "", "#painter"), + + // StyleRead("fill:url(#painter) inherit", "", "#painter"), + + StyleRead("fill:inherit"), + + + // General tests (in general order of appearance in sp_style_read), SPIPaint tested above + StyleRead("visibility:hidden"), // SPIEnum + StyleRead("visibility:collapse"), StyleRead("visibility:visible"), + StyleRead("display:none"), // SPIEnum + StyleRead("overflow:visible"), // SPIEnum + StyleRead("overflow:auto"), // SPIEnum + + StyleRead("color:#ff0000"), StyleRead("color:blue", "color:#0000ff"), + // StyleRead("color:currentColor"), SVG 1.1 does not allow color value 'currentColor' + + // Font shorthand + StyleRead("font:bold 12px Arial", "font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;" + "font-size:12px;line-height:normal;font-family:Arial"), + StyleRead("font:bold 12px/24px 'Times New Roman'", + "font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12px;line-" + "height:24px;font-family:\'Times New Roman\'"), + + // From CSS 3 Fonts (examples): + StyleRead("font: 12pt/15pt sans-serif", "font-style:normal;font-variant:normal;font-weight:normal;font-stretch:" + "normal;font-size:16px;line-height:15pt;font-family:sans-serif"), + // StyleRead("font: 80% sans-serif", + // "font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:80%;line-height:normal;font-family:sans-serif"), + // StyleRead("font: x-large/110% 'new century schoolbook', serif", + // "font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:x-large;line-height:110%;font-family:\'new + //century schoolbook\', serif"), + StyleRead("font: bold italic large Palatino, serif", + "font-style:italic;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:large;line-" + "height:normal;font-family:Palatino, serif"), + // StyleRead("font: normal small-caps 120%/120% fantasy", + // "font-style:normal;font-variant:small-caps;font-weight:normal;font-stretch:normal;font-size:120%;line-height:120%;font-family:fantasy"), + StyleRead("font: condensed oblique 12pt 'Helvetica Neue', serif;", + "font-style:oblique;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:16px;" + "line-height:normal;font-family:\'Helvetica Neue\', serif"), + + StyleRead("font-family:sans-serif"), // SPIString, text_private + StyleRead("font-family:Arial"), + // StyleRead("font-variant:normal;font-stretch:normal;-inkscape-font-specification:Nimbus Roman No9 L Bold + // Italic"), + + // Needs to be fixed (quotes should be around each font-family): + StyleRead("font-family:Georgia, 'Minion Web'", "font-family:Georgia, \'Minion Web\'"), + StyleRead("font-size:12", "font-size:12px"), // SPIFontSize + StyleRead("font-size:12px"), StyleRead("font-size:12pt", "font-size:16px"), StyleRead("font-size:medium"), + StyleRead("font-size:smaller"), + StyleRead("font-style:italic"), // SPIEnum + StyleRead("font-variant:small-caps"), // SPIEnum + StyleRead("font-weight:100"), // SPIEnum + StyleRead("font-weight:normal"), StyleRead("font-weight:bolder"), + StyleRead("font-stretch:condensed"), // SPIEnum + + StyleRead("font-variant-ligatures:none"), // SPILigatures + StyleRead("font-variant-ligatures:normal"), StyleRead("font-variant-ligatures:no-common-ligatures"), + StyleRead("font-variant-ligatures:discretionary-ligatures"), + StyleRead("font-variant-ligatures:historical-ligatures"), StyleRead("font-variant-ligatures:no-contextual"), + StyleRead("font-variant-ligatures:common-ligatures", "font-variant-ligatures:normal"), + StyleRead("font-variant-ligatures:contextual", "font-variant-ligatures:normal"), + StyleRead("font-variant-ligatures:no-common-ligatures historical-ligatures"), + StyleRead("font-variant-ligatures:historical-ligatures no-contextual"), + StyleRead("font-variant-position:normal"), StyleRead("font-variant-position:sub"), + StyleRead("font-variant-position:super"), StyleRead("font-variant-caps:normal"), + StyleRead("font-variant-caps:small-caps"), StyleRead("font-variant-caps:all-small-caps"), + StyleRead("font-variant-numeric:normal"), StyleRead("font-variant-numeric:lining-nums"), + StyleRead("font-variant-numeric:oldstyle-nums"), StyleRead("font-variant-numeric:proportional-nums"), + StyleRead("font-variant-numeric:tabular-nums"), StyleRead("font-variant-numeric:diagonal-fractions"), + StyleRead("font-variant-numeric:stacked-fractions"), StyleRead("font-variant-numeric:ordinal"), + StyleRead("font-variant-numeric:slashed-zero"), StyleRead("font-variant-numeric:tabular-nums slashed-zero"), + StyleRead("font-variant-numeric:tabular-nums proportional-nums", "font-variant-numeric:proportional-nums"), + + StyleRead("font-variation-settings:'wght' 400"), + StyleRead("font-variation-settings:'wght' 400", "font-variation-settings:'wght' 400"), + StyleRead("font-variation-settings:'wght' 400, 'slnt' 0.5", "font-variation-settings:'slnt' 0.5, 'wght' 400"), + StyleRead("font-variation-settings:\"wght\" 400", "font-variation-settings:'wght' 400"), + + // Should be moved down + StyleRead("text-indent:12em"), // SPILength? + StyleRead("text-align:center"), // SPIEnum + + // SPITextDecoration + // The default value for 'text-decoration-color' is 'currentColor', but + // we cannot set the default to that value yet. (We need to switch + // SPIPaint to SPIColor and then add the ability to set default.) + // StyleRead("text-decoration: underline", + // "text-decoration: underline;text-decoration-line: underline;text-decoration-color:currentColor"), + // StyleRead("text-decoration: overline underline", + // "text-decoration: underline overline;text-decoration-line: underline + // overline;text-decoration-color:currentColor"), + + StyleRead("text-decoration: underline wavy #0000ff", + "text-decoration:underline;text-decoration-line:" + "underline;text-decoration-style:wavy;text-decoration-color:#0000ff"), + StyleRead("text-decoration: double overline underline #ff0000", + "text-decoration:underline overline;text-decoration-line:underline " + "overline;text-decoration-style:double;text-decoration-color:#ff0000"), + + // SPITextDecorationLine + // If only "text-decoration-line" is set but not "text-decoration", don't write "text-decoration" (changed in 1.1) + StyleRead("text-decoration-line:underline", "text-decoration-line:underline"), + // "text-decoration" overwrites "text-decoration-line" and vice versa, last one counts + StyleRead("text-decoration-line:overline;text-decoration:underline", + "text-decoration:underline;text-decoration-line:underline"), + StyleRead("text-decoration:underline;text-decoration-line:overline", + "text-decoration:overline;text-decoration-line:overline"), + + // SPITextDecorationStyle + StyleRead("text-decoration-style:solid"), StyleRead("text-decoration-style:dotted"), + + // SPITextDecorationColor + StyleRead("text-decoration-color:#ff00ff"), + + // Should be moved up + StyleRead("line-height:24px"), // SPILengthOrNormal + StyleRead("line-height:1.5"), + StyleRead("letter-spacing:2px"), // SPILengthOrNormal + StyleRead("word-spacing:2px"), // SPILengthOrNormal + StyleRead("word-spacing:normal"), + StyleRead("text-transform:lowercase"), // SPIEnum + // ... + StyleRead("baseline-shift:baseline"), // SPIBaselineShift + StyleRead("baseline-shift:sub"), StyleRead("baseline-shift:12.5%"), StyleRead("baseline-shift:2px"), + + StyleRead("opacity:0.1"), // SPIScale24 + // ... + StyleRead("stroke-width:2px"), // SPILength + StyleRead("stroke-linecap:round"), // SPIEnum + StyleRead("stroke-linejoin:round"), // SPIEnum + StyleRead("stroke-miterlimit:4"), // SPIFloat + StyleRead("marker:url(#Arrow)"), // SPIString + StyleRead("marker-start:url(#Arrow)"), StyleRead("marker-mid:url(#Arrow)"), StyleRead("marker-end:url(#Arrow)"), + StyleRead("stroke-opacity:0.5"), // SPIScale24 + // Currently inkscape handle unit conversion in dasharray but need + // a active document to do it, so we can't include in any test + StyleRead("stroke-dasharray:0, 1, 0, 1"), // SPIDashArray + StyleRead("stroke-dasharray:0 1 0 1", "stroke-dasharray:0, 1, 0, 1"), + StyleRead("stroke-dasharray:0 1 2 3", "stroke-dasharray:0, 1, 2, 3"), + StyleRead("stroke-dashoffset:13"), // SPILength + StyleRead("stroke-dashoffset:10px"), + // ... + // StyleRead("filter:url(#myfilter)"), // SPIFilter segfault in read + StyleRead("filter:inherit"), + + StyleRead("opacity:0.1;fill:#ff0000;stroke:#0000ff;stroke-width:2px"), + StyleRead("opacity:0.1;fill:#ff0000;stroke:#0000ff;stroke-width:2px;stroke-dasharray:1, 2, 3, " + "4;stroke-dashoffset:15"), + + StyleRead("paint-order:stroke"), // SPIPaintOrder + StyleRead("paint-order:normal"), + StyleRead("paint-order: markers stroke fill", "paint-order:markers stroke fill"), + + // !important (in order of appearance in style-internal.h) + StyleRead("stroke-miterlimit:4 !important"), // SPIFloat + StyleRead("stroke-opacity:0.5 !important"), // SPIScale24 + StyleRead("stroke-width:2px !important"), // SPILength + StyleRead("line-height:24px !important"), // SPILengthOrNormal + StyleRead("line-height:normal !important"), + StyleRead("font-stretch:condensed !important"), // SPIEnum + StyleRead("marker:url(#Arrow) !important"), // SPIString + StyleRead("color:#0000ff !important"), // SPIColor + StyleRead("fill:none !important"), // SPIPaint + StyleRead("fill:currentColor !important"), StyleRead("fill:#ff00ff !important"), + StyleRead("paint-order:stroke !important"), // SPIPaintOrder + StyleRead("paint-order:normal !important"), + StyleRead("stroke-dasharray:0, 1, 0, 1 !important"), // SPIDashArray + StyleRead("font-size:12px !important"), // SPIFontSize + StyleRead("baseline-shift:baseline !important"), // SPIBaselineShift + StyleRead("baseline-shift:sub !important"), + // StyleRead("text-decoration-line: underline !important"), // SPITextDecorationLine + + }; + + size_t count = sizeof(all_style_data) / sizeof(all_style_data[0]); + std::vector vect(all_style_data, all_style_data + count); + return vect; +} + +TEST(StyleTest, Read) { + std::vector all_style = getStyleData(); + EXPECT_GT(all_style.size(), 0); + for (auto i : all_style) { + + SPStyle style; + style.mergeString (i.src.c_str()); + + if (!i.uri.empty()) { + //EXPECT_EQ (style.fill.value.href->getURI()->toString(), i.uri); + } + + std::string out = style.write(); + if (i.dst.empty()) { + // std::cout << "out: " << out << std::endl; + // std::cout << "i.src: " << i.src << std::endl; + EXPECT_EQ (out, i.src); + } else { + // std::cout << "out: " << out << std::endl; + // std::cout << "i.dst: " << i.dst << std::endl; + EXPECT_EQ (out, i.dst); + } + } +} + + +// ------------------------------------------------------------------------------------ + +class StyleMatch { + +public: + StyleMatch(std::string src, std::string dst, bool const &match) : + src(std::move(src)), dst(std::move(dst)), match(match) + { + } + + std::string src; + std::string dst; + bool match; + +}; + +std::vector getStyleMatchData() +{ + StyleMatch all_style_data[] = { + + // SPIFloat + StyleMatch("stroke-miterlimit:4", "stroke-miterlimit:4", true ), + StyleMatch("stroke-miterlimit:4", "stroke-miterlimit:2", false), + StyleMatch("stroke-miterlimit:4", "", true ), // Default + + // SPIScale24 + StyleMatch("opacity:0.3", "opacity:0.3", true ), + StyleMatch("opacity:0.3", "opacity:0.6", false), + StyleMatch("opacity:1.0", "", true ), // Default + + // SPILength + StyleMatch("text-indent:3", "text-indent:3", true ), + StyleMatch("text-indent:6", "text-indent:3", false), + StyleMatch("text-indent:6px", "text-indent:3", false), + StyleMatch("text-indent:1px", "text-indent:12pc", false), + StyleMatch("text-indent:2ex", "text-indent:2ex", false), + + // SPILengthOrNormal + StyleMatch("letter-spacing:normal", "letter-spacing:normal", true ), + StyleMatch("letter-spacing:2", "letter-spacing:normal", false), + StyleMatch("letter-spacing:normal", "letter-spacing:2", false), + StyleMatch("letter-spacing:5px", "letter-spacing:5px", true ), + StyleMatch("letter-spacing:10px", "letter-spacing:5px", false), + StyleMatch("letter-spacing:10em", "letter-spacing:10em", false), + + // SPIEnum + StyleMatch("text-anchor:start", "text-anchor:start", true ), + StyleMatch("text-anchor:start", "text-anchor:middle", false), + StyleMatch("text-anchor:start", "", true ), // Default + StyleMatch("text-anchor:start", "text-anchor:junk", true ), // Bad value + + StyleMatch("font-weight:normal", "font-weight:400", true ), + StyleMatch("font-weight:bold", "font-weight:700", true ), + + + // SPIString and SPIFontString + StyleMatch("font-family:Arial", "font-family:Arial", true ), + StyleMatch("font-family:A B", "font-family:A B", true ), + StyleMatch("font-family:A B", "font-family:A C", false), + // Default is not set by class... value is NULL which cannot be compared + // StyleMatch("font-family:sans-serif", "", true ), // Default + + // SPIColor + StyleMatch("color:blue", "color:blue", true ), + StyleMatch("color:blue", "color:red", false), + StyleMatch("color:red", "color:#ff0000", true ), + + // SPIPaint + StyleMatch("fill:blue", "fill:blue", true ), + StyleMatch("fill:blue", "fill:red", false), + StyleMatch("fill:currentColor", "fill:currentColor", true ), + StyleMatch("fill:url(#xxx)", "fill:url(#xxx)", true ), + // Needs URL defined as in test 1 + //StyleMatch("fill:url(#xxx)", "fill:url(#yyy)", false), + + // SPIPaintOrder + StyleMatch("paint-order:markers", "paint-order:markers", true ), + StyleMatch("paint-order:markers", "paint-order:stroke", false), + //StyleMatch("paint-order:fill stroke markers", "", true ), // Default + StyleMatch("paint-order:normal", "paint-order:normal", true ), + //StyleMatch("paint-order:fill stroke markers", "paint-order:normal", true ), + + // SPIDashArray + StyleMatch("stroke-dasharray:0 1 2 3","stroke-dasharray:0 1 2 3",true ), + StyleMatch("stroke-dasharray:0 1", "stroke-dasharray:0 2", false), + + // SPIFilter + + // SPIFontSize + StyleMatch("font-size:12px", "font-size:12px", true ), + StyleMatch("font-size:12px", "font-size:24px", false), + StyleMatch("font-size:12ex", "font-size:24ex", false), + StyleMatch("font-size:medium", "font-size:medium", true ), + StyleMatch("font-size:medium", "font-size:large", false), + + // SPIBaselineShift + StyleMatch("baseline-shift:baseline", "baseline-shift:baseline", true ), + StyleMatch("baseline-shift:sub", "baseline-shift:sub", true ), + StyleMatch("baseline-shift:sub", "baseline-shift:super", false), + StyleMatch("baseline-shift:baseline", "baseline-shift:sub", false), + StyleMatch("baseline-shift:10px", "baseline-shift:10px", true ), + StyleMatch("baseline-shift:10px", "baseline-shift:12px", false), + + + // SPITextDecorationLine + StyleMatch("text-decoration-line:underline", "text-decoration-line:underline", true ), + StyleMatch("text-decoration-line:underline", "text-decoration-line:overline", false), + StyleMatch("text-decoration-line:underline overline", "text-decoration-line:underline overline", true ), + StyleMatch("text-decoration-line:none", "", true ), // Default + + + // SPITextDecorationStyle + StyleMatch("text-decoration-style:solid", "text-decoration-style:solid", true ), + StyleMatch("text-decoration-style:dotted", "text-decoration-style:solid", false), + StyleMatch("text-decoration-style:solid", "", true ), // Default + + // SPITextDecoration + StyleMatch("text-decoration:underline", "text-decoration:underline", true ), + StyleMatch("text-decoration:underline", "text-decoration:overline", false), + StyleMatch("text-decoration:underline overline","text-decoration:underline overline",true ), + StyleMatch("text-decoration:overline underline","text-decoration:underline overline",true ), + // StyleMatch("text-decoration:none", "text-decoration-color:currentColor", true ), // Default + + }; + + size_t count = sizeof(all_style_data) / sizeof(all_style_data[0]); + std::vector vect(all_style_data, all_style_data + count); + return vect; +} + +TEST(StyleTest, Match) { + std::vector all_style = getStyleMatchData(); + EXPECT_GT(all_style.size(), 0); + for (auto i : all_style) { + + SPStyle style_src; + SPStyle style_dst; + + style_src.mergeString( i.src.c_str() ); + style_dst.mergeString( i.dst.c_str() ); + + // std::cout << "Test:" << std::endl; + // std::cout << " C: |" << i.src + // << "| |" << i.dst << "|" << std::endl; + // std::cout << " S: |" << style_src.write( SP_STYLE_FLAG_IFSET ) + // << "| |" << style_dst.write( SP_STYLE_FLAG_IFSET ) << "|" <result == this->parent ? "" : this->result); + } + + std::string parent; + std::string child; + std::string result; + std::string diff; + +}; + +std::vector getStyleCascadeData() +{ + + StyleCascade all_style_data[] = { + + // SPIFloat + StyleCascade("stroke-miterlimit:6", "stroke-miterlimit:2", "stroke-miterlimit:2" ), + StyleCascade("stroke-miterlimit:6", "", "stroke-miterlimit:6" ), + StyleCascade("", "stroke-miterlimit:2", "stroke-miterlimit:2" ), + + // SPIScale24 + StyleCascade("opacity:0.3", "opacity:0.3", "opacity:0.3", "opacity:0.3" ), + StyleCascade("opacity:0.3", "opacity:0.6", "opacity:0.6" ), + // 'opacity' does not inherit + StyleCascade("opacity:0.3", "", "opacity:1" ), + StyleCascade("", "opacity:0.3", "opacity:0.3" ), + StyleCascade("opacity:0.5", "opacity:inherit", "opacity:0.5", "opacity:0.5" ), + StyleCascade("", "", "opacity:1" ), + + // SPILength + StyleCascade("text-indent:3", "text-indent:3", "text-indent:3" ), + StyleCascade("text-indent:6", "text-indent:3", "text-indent:3" ), + StyleCascade("text-indent:6px", "text-indent:3", "text-indent:3" ), + StyleCascade("text-indent:1px", "text-indent:12pc", "text-indent:12pc" ), + // ex, em cannot be equal + //StyleCascade("text-indent:2ex", "text-indent:2ex", "text-indent:2ex" ), + StyleCascade("text-indent:3", "", "text-indent:3" ), + StyleCascade("text-indent:3", "text-indent:inherit", "text-indent:3" ), + + // SPILengthOrNormal + StyleCascade("letter-spacing:normal", "letter-spacing:normal", "letter-spacing:normal" ), + StyleCascade("letter-spacing:2", "letter-spacing:normal", "letter-spacing:normal" ), + StyleCascade("letter-spacing:normal", "letter-spacing:2", "letter-spacing:2" ), + StyleCascade("letter-spacing:5px", "letter-spacing:5px", "letter-spacing:5px" ), + StyleCascade("letter-spacing:10px", "letter-spacing:5px", "letter-spacing:5px" ), + // ex, em cannot be equal + // StyleCascade("letter-spacing:10em", "letter-spacing:10em", "letter-spacing:10em" ), + + // SPIEnum + StyleCascade("text-anchor:start", "text-anchor:start", "text-anchor:start" ), + StyleCascade("text-anchor:start", "text-anchor:middle", "text-anchor:middle" ), + StyleCascade("text-anchor:start", "", "text-anchor:start" ), + StyleCascade("text-anchor:start", "text-anchor:junk", "text-anchor:start" ), + StyleCascade("text-anchor:end", "text-anchor:inherit", "text-anchor:end" ), + + StyleCascade("font-weight:400", "font-weight:400", "font-weight:400" ), + StyleCascade("font-weight:400", "font-weight:700", "font-weight:700" ), + StyleCascade("font-weight:400", "font-weight:bolder", "font-weight:700" ), + StyleCascade("font-weight:700", "font-weight:bolder", "font-weight:900" ), + StyleCascade("font-weight:400", "font-weight:lighter", "font-weight:100" ), + StyleCascade("font-weight:200", "font-weight:lighter", "font-weight:100" ), + + StyleCascade("font-stretch:condensed","font-stretch:expanded", "font-stretch:expanded" ), + StyleCascade("font-stretch:condensed","font-stretch:wider", "font-stretch:semi-condensed" ), + + // SPIString and SPIFontString + + StyleCascade("font-variation-settings:'wght' 400", "", "font-variation-settings:'wght' 400"), + StyleCascade("font-variation-settings:'wght' 100", + "font-variation-settings:'wght' 400", + "font-variation-settings:'wght' 400"), + + StyleCascade("font-variant-ligatures:no-common-ligatures", "", "font-variant-ligatures:no-common-ligatures"), + StyleCascade("font-variant-ligatures:no-common-ligatures", "inherit", "font-variant-ligatures:no-common-ligatures"), + StyleCascade("font-variant-ligatures:normal", "font-variant-ligatures:no-common-ligatures", "font-variant-ligatures:no-common-ligatures"), + StyleCascade("", "font-variant-ligatures:no-common-ligatures", "font-variant-ligatures:no-common-ligatures"), + + // SPIPaint + + // SPIPaintOrder + + // SPIDashArray + + // SPIFilter + + // SPIFontSize + + // SPIBaselineShift + + + // SPITextDecorationLine + StyleCascade("text-decoration-line:overline", "text-decoration-line:underline", + "text-decoration-line:underline" ), + StyleCascade("text-decoration:overline", + "text-decoration:underline", + "text-decoration:underline;text-decoration-line:underline"), + StyleCascade("text-decoration:underline", + "text-decoration:underline", + "text-decoration:underline;text-decoration-line:underline", + ""), + StyleCascade("text-decoration:overline;text-decoration-line:underline", + "text-decoration:overline", + "text-decoration:overline;text-decoration-line:overline"), + StyleCascade("text-decoration:overline;text-decoration-line:underline", + "text-decoration:underline", + "text-decoration:underline;text-decoration-line:underline", + ""), + + // SPITextDecorationStyle + + // SPITextDecoration + }; + + size_t count = sizeof(all_style_data) / sizeof(all_style_data[0]); + std::vector vect(all_style_data, all_style_data + count); + return vect; + +} + +TEST(StyleTest, Cascade) { + std::vector all_style = getStyleCascadeData(); + EXPECT_GT(all_style.size(), 0); + for (auto i : all_style) { + + SPStyle style_parent; + SPStyle style_child; + SPStyle style_result; + + style_parent.mergeString( i.parent.c_str() ); + style_child.mergeString( i.child.c_str() ); + style_result.mergeString( i.result.c_str() ); + + // std::cout << "Test:" << std::endl; + // std::cout << " Input: "; + // std::cout << " Parent: " << i.parent + // << " Child: " << i.child + // << " Result: " << i.result << std::endl; + // std::cout << " Write: "; + // std::cout << " Parent: " << style_parent.write( SP_STYLE_FLAG_IFSET ) + // << " Child: " << style_child.write( SP_STYLE_FLAG_IFSET ) + // << " Result: " << style_result.write( SP_STYLE_FLAG_IFSET ) << std::endl; + + style_child.cascade( &style_parent ); + + EXPECT_TRUE(style_child == style_result ); + + // if diff + EXPECT_STREQ(style_result.writeIfDiff(nullptr).c_str(), i.result.c_str()); + EXPECT_STREQ(style_result.writeIfDiff(&style_parent).c_str(), i.diff.c_str()); + } +} + + +} // namespace + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/testfiles/src/svg-affine-test.cpp b/testfiles/src/svg-affine-test.cpp new file mode 100644 index 0000000..300242d --- /dev/null +++ b/testfiles/src/svg-affine-test.cpp @@ -0,0 +1,226 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** @file + * Test for SVG colors + *//* + * Authors: see git history + * + * Copyright (C) 2010 Authors + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ +#include +#include +#include +#include + +#include "svg/svg.h" +#include <2geom/affine.h> + + +struct test_t +{ + char const *str; + Geom::Affine matrix; +}; + +static double const DEGREE = M_PI / 180.; + +test_t const read_matrix_tests[5] = {{"matrix(0,0,0,0,0,0)", Geom::Affine(0, 0, 0, 0, 0, 0)}, + {" matrix(1,2,3,4,5,6)", Geom::Affine(1, 2, 3, 4, 5, 6)}, + {"matrix (1 2 -3,-4,5e6,-6e-7)", Geom::Affine(1, 2, -3, -4, 5e6, -6e-7)}, + {"matrix(1,2,3,4,5e6-3)", Geom::Affine(1, 2, 3, 4, 5e6, -3)}, + {"matrix(1,2,3,4,5e6.3)", Geom::Affine(1, 2, 3, 4, 5e6, 0.3)}}; +test_t const read_translate_tests[3] = {{"translate(1)", Geom::Affine(1, 0, 0, 1, 1, 0)}, + {"translate(1,1)", Geom::Affine(1, 0, 0, 1, 1, 1)}, + {"translate(-1e3 .123e2)", Geom::Affine(1, 0, 0, 1, -1e3, .123e2)}}; +test_t const read_scale_tests[3] = {{"scale(2)", Geom::Affine(2, 0, 0, 2, 0, 0)}, + {"scale(2,3)", Geom::Affine(2, 0, 0, 3, 0, 0)}, + {"scale(0.1e-2 -.475e0)", Geom::Affine(0.1e-2, 0, 0, -.475e0, 0, 0)}}; +test_t const read_rotate_tests[4] = { + {"rotate(13 )", Geom::Affine(cos(13. * DEGREE), sin(13. * DEGREE), -sin(13. * DEGREE), cos(13. * DEGREE), 0, 0)}, + {"rotate(-13)", + Geom::Affine(cos(-13. * DEGREE), sin(-13. * DEGREE), -sin(-13. * DEGREE), cos(-13. * DEGREE), 0, 0)}, + {"rotate(373)", Geom::Affine(cos(13. * DEGREE), sin(13. * DEGREE), -sin(13. * DEGREE), cos(13. * DEGREE), 0, 0)}, + {"rotate(13,7,11)", Geom::Affine(cos(13. * DEGREE), sin(13. * DEGREE), -sin(13. * DEGREE), cos(13. * DEGREE), + (1 - cos(13. * DEGREE)) * 7 + sin(13. * DEGREE) * 11, + (1 - cos(13. * DEGREE)) * 11 - sin(13. * DEGREE) * 7)}}; +test_t const read_skew_tests[3] = {{"skewX( 30)", Geom::Affine(1, 0, tan(30. * DEGREE), 1, 0, 0)}, + {"skewX(-30)", Geom::Affine(1, 0, tan(-30. * DEGREE), 1, 0, 0)}, + {"skewY(390)", Geom::Affine(1, tan(30. * DEGREE), 0, 1, 0, 0)}}; +char const *const read_fail_tests[25] = { + "matrix((1,2,3,4,5,6)", + "matrix((1,2,3,4,5,6))", + "matrix(1,2,3,4,5,6))", + "matrix(,1,2,3,4,5,6)", + "matrix(1,2,3,4,5,6,)", + "matrix(1,2,3,4,5,)", + "matrix(1,2,3,4,5)", + "translate()", + "translate(,)", + "translate(1,)", + "translate(1,6,)", + "translate(1,6,0)", + "scale()", + "scale(1,6,2)", + "rotate()", + "rotate(1,6)", + "rotate(1,6,)", + "rotate(1,6,3,4)", + "skewX()", + "skewX(-)", + "skewX(.)", + "skewY(,)", + "skewY(1,2)"}; +test_t const write_matrix_tests[2] = { + {"matrix(1,2,3,4,5,6)", Geom::Affine(1, 2, 3, 4, 5, 6)}, + {"matrix(-1,2123,3,0.4,1e-8,1e20)", Geom::Affine(-1, 2.123e3, 3 + 1e-14, 0.4, 1e-8, 1e20)}}; +test_t const write_translate_tests[3] = {{"translate(1,1)", Geom::Affine(1, 0, 0, 1, 1, 1)}, + {"translate(1)", Geom::Affine(1, 0, 0, 1, 1, 0)}, + {"translate(-1345,0.123)", Geom::Affine(1, 0, 0, 1, -1.345e3, .123)}}; +test_t const write_scale_tests[3] = {{"scale(0)", Geom::Affine(0, 0, 0, 0, 0, 0)}, + {"scale(7)", Geom::Affine(7, 0, 0, 7, 0, 0)}, + {"scale(2,3)", Geom::Affine(2, 0, 0, 3, 0, 0)}}; +test_t const write_rotate_tests[3] = { + {"rotate(13)", Geom::Affine(cos(13. * DEGREE), sin(13. * DEGREE), -sin(13. * DEGREE), cos(13. * DEGREE), 0, 0)}, + {"rotate(-13,7,11)", Geom::Affine(cos(-13. * DEGREE), sin(-13. * DEGREE), -sin(-13. * DEGREE), cos(-13. * DEGREE), + (1 - cos(-13. * DEGREE)) * 7 + sin(-13. * DEGREE) * 11, + (1 - cos(-13. * DEGREE)) * 11 - sin(-13. * DEGREE) * 7)}, + {"rotate(-34.5,6.7,89)", + Geom::Affine(cos(-34.5 * DEGREE), sin(-34.5 * DEGREE), -sin(-34.5 * DEGREE), cos(-34.5 * DEGREE), + (1 - cos(-34.5 * DEGREE)) * 6.7 + sin(-34.5 * DEGREE) * 89, + (1 - cos(-34.5 * DEGREE)) * 89 - sin(-34.5 * DEGREE) * 6.7)}}; +test_t const write_skew_tests[3] = {{"skewX(30)", Geom::Affine(1, 0, tan(30. * DEGREE), 1, 0, 0)}, + {"skewX(-30)", Geom::Affine(1, 0, tan(-30. * DEGREE), 1, 0, 0)}, + {"skewY(30)", Geom::Affine(1, tan(30. * DEGREE), 0, 1, 0, 0)}}; + +bool approx_equal_pred(Geom::Affine const &ref, Geom::Affine const &cm) +{ + double maxabsdiff = 0; + for (size_t i = 0; i < 6; i++) { + maxabsdiff = std::max(std::abs(ref[i] - cm[i]), maxabsdiff); + } + return maxabsdiff < 1e-14; +} + +TEST(SvgAffineTest, testReadIdentity) +{ + char const *strs[] = {// 0, + " ", "", "matrix(1,0,0,1,0,0)", "translate(0,0)", "scale(1,1)", "rotate(0,0,0)", "skewX(0)", + "skewY(0)"}; + size_t n = G_N_ELEMENTS(strs); + for (size_t i = 0; i < n; i++) { + Geom::Affine cm; + EXPECT_TRUE(sp_svg_transform_read(strs[i], &cm)) << i; + ASSERT_EQ(Geom::identity(), cm) << strs[i]; + } +} + +TEST(SvgAffineTest, testWriteIdentity) +{ + auto str = sp_svg_transform_write(Geom::identity()); + ASSERT_TRUE(str == ""); +} + +TEST(SvgAffineTest, testReadMatrix) +{ + for (size_t i = 0; i < G_N_ELEMENTS(read_matrix_tests); i++) { + Geom::Affine cm; + ASSERT_TRUE(sp_svg_transform_read(read_matrix_tests[i].str, &cm)) << read_matrix_tests[i].str; + ASSERT_TRUE(approx_equal_pred(read_matrix_tests[i].matrix, cm)) << read_matrix_tests[i].str; + } +} + +TEST(SvgAffineTest, testReadTranslate) +{ + for (size_t i = 0; i < G_N_ELEMENTS(read_translate_tests); i++) { + Geom::Affine cm; + ASSERT_TRUE(sp_svg_transform_read(read_translate_tests[i].str, &cm)) << read_translate_tests[i].str; + ASSERT_TRUE(approx_equal_pred(read_translate_tests[i].matrix, cm)) << read_translate_tests[i].str; + } +} + +TEST(SvgAffineTest, testReadScale) +{ + for (size_t i = 0; i < G_N_ELEMENTS(read_scale_tests); i++) { + Geom::Affine cm; + ASSERT_TRUE(sp_svg_transform_read(read_scale_tests[i].str, &cm)) << read_scale_tests[i].str; + ASSERT_TRUE(approx_equal_pred(read_scale_tests[i].matrix, cm)) << read_scale_tests[i].str; + } +} + +TEST(SvgAffineTest, testReadRotate) +{ + for (size_t i = 0; i < G_N_ELEMENTS(read_rotate_tests); i++) { + Geom::Affine cm; + ASSERT_TRUE(sp_svg_transform_read(read_rotate_tests[i].str, &cm)) << read_rotate_tests[i].str; + ASSERT_TRUE(approx_equal_pred(read_rotate_tests[i].matrix, cm)) << read_rotate_tests[i].str; + } +} + +TEST(SvgAffineTest, testReadSkew) +{ + for (size_t i = 0; i < G_N_ELEMENTS(read_skew_tests); i++) { + Geom::Affine cm; + ASSERT_TRUE(sp_svg_transform_read(read_skew_tests[i].str, &cm)) << read_skew_tests[i].str; + ASSERT_TRUE(approx_equal_pred(read_skew_tests[i].matrix, cm)) << read_skew_tests[i].str; + } +} + +TEST(SvgAffineTest, testWriteMatrix) +{ + for (size_t i = 0; i < G_N_ELEMENTS(write_matrix_tests); i++) { + auto str = sp_svg_transform_write(write_matrix_tests[i].matrix); + ASSERT_TRUE(!strcmp(str.c_str(), write_matrix_tests[i].str)); + } +} + +TEST(SvgAffineTest, testWriteTranslate) +{ + for (size_t i = 0; i < G_N_ELEMENTS(write_translate_tests); i++) { + auto str = sp_svg_transform_write(write_translate_tests[i].matrix); + ASSERT_TRUE(!strcmp(str.c_str(), write_translate_tests[i].str)); + } +} + +TEST(SvgAffineTest, testWriteScale) +{ + for (size_t i = 0; i < G_N_ELEMENTS(write_scale_tests); i++) { + auto str = sp_svg_transform_write(write_scale_tests[i].matrix); + ASSERT_TRUE(!strcmp(str.c_str(), write_scale_tests[i].str)); + } +} + +TEST(SvgAffineTest, testWriteRotate) +{ + for (size_t i = 0; i < G_N_ELEMENTS(write_rotate_tests); i++) { + auto str = sp_svg_transform_write(write_rotate_tests[i].matrix); + ASSERT_TRUE(!strcmp(str.c_str(), write_rotate_tests[i].str)); + } +} + +TEST(SvgAffineTest, testWriteSkew) +{ + for (size_t i = 0; i < G_N_ELEMENTS(write_skew_tests); i++) { + auto str = sp_svg_transform_write(write_skew_tests[i].matrix); + ASSERT_TRUE(!strcmp(str.c_str(), write_skew_tests[i].str)); + } +} + +TEST(SvgAffineTest, testReadConcatenation) +{ + char const *str = "skewY(17)skewX(9)translate(7,13)scale(2)rotate(13)translate(3,5)"; + Geom::Affine ref(2.0199976232558053, 1.0674773585906016, -0.14125199392774669, 1.9055550612095459, + 14.412730624347654, 28.499820929377454); // Precomputed using Mathematica + Geom::Affine cm; + ASSERT_TRUE(sp_svg_transform_read(str, &cm)); + ASSERT_TRUE(approx_equal_pred(ref, cm)); +} + +TEST(SvgAffineTest, testReadFailures) +{ + for (size_t i = 0; i < G_N_ELEMENTS(read_fail_tests); i++) { + Geom::Affine cm; + EXPECT_FALSE(sp_svg_transform_read(read_fail_tests[i], &cm)) << read_fail_tests[i]; + } +} + +// vim: filetype=cpp:expandtab:shiftwidth=4:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/testfiles/src/svg-box-test.cpp b/testfiles/src/svg-box-test.cpp new file mode 100644 index 0000000..f3fe67b --- /dev/null +++ b/testfiles/src/svg-box-test.cpp @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** @file + * Test for SVG box + *//* + * Authors: see git history + * + * Copyright (C) 2010 Authors + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ +#include "svg/svg-box.h" +#include "svg/svg.h" + +#include +#include +#include +#include + +struct read_test_t +{ + const std::string str; + int top; + int right; + int bottom; + int left; +}; +struct write_test_t +{ + const std::string in; + const std::string out; +}; + +// clang-format off +read_test_t read_tests[5] = { + {"0", 0, 0, 0, 0}, + {"1", 1, 1, 1, 1}, + {"1 2 3 4", 1, 2, 3, 4}, + {"1,2,3,4", 1, 2, 3, 4}, + {"2cm 4cm", 76, 151, 76, 151}, +}; +const char* fail_tests[4] = { + "", + "a b c d", + "12miles", + "14mmm", +}; +write_test_t write_tests[7] = { + {"0", "0"}, + {"1", "1"}, + {"1 1 1 1", "1"}, + {"1cm", "1cm"}, + {"4cm 2in", "4cm 2in"}, + {"7 2 4cm", "7 2 4cm"}, + {"1,2,3", "1 2 3"}, +}; +read_test_t set_tests[3] = { + {"1", 1, 1, 1, 1}, + {"1 2", 1, 2, 1, 2}, + {"1 2 3 4", 1, 2, 3, 4}, +}; +// clang-format on + +TEST(SvgBoxTest, testRead) +{ + for (size_t i = 0; i < G_N_ELEMENTS(read_tests); i++) { + SVGBox box; + ASSERT_TRUE(box.read(read_tests[i].str)) << read_tests[i].str; + ASSERT_EQ(round(box.top().computed), read_tests[i].top) << read_tests[i].str; + ASSERT_EQ(round(box.right().computed), read_tests[i].right) << read_tests[i].str; + ASSERT_EQ(round(box.bottom().computed), read_tests[i].bottom) << read_tests[i].str; + ASSERT_EQ(round(box.left().computed), read_tests[i].left) << read_tests[i].str; + } +} + +TEST(SvgBoxTest, testFailures) +{ + for (size_t i = 0; i < G_N_ELEMENTS(fail_tests); i++) { + SVGLength box; + ASSERT_TRUE( !box.read(fail_tests[i])) << fail_tests[i]; + } +} + +TEST(SvgBoxTest, testWrite) +{ + for (size_t i = 0; i < G_N_ELEMENTS(write_tests); i++) { + SVGBox box; + ASSERT_TRUE(box.read(write_tests[i].in)) << write_tests[i].in; + ASSERT_EQ(box.write(), write_tests[i].out) << write_tests[i].in; + } +} + +TEST(SvgBoxTest, testSet) +{ + for (auto t : set_tests) { + SVGBox box; + box.set(t.top, t.right, t.bottom, t.left); + ASSERT_EQ(box.write(), t.str); + } +} + +TEST(SvgBoxTest, testToFromString) +{ + SVGBox box; + ASSERT_TRUE(box.fromString("10mm 5", "mm")); + ASSERT_EQ(box.toString("mm"), "10mm 5.0000001mm"); +} + +TEST(SvgBoxTest, testConfine) +{ + SVGBox box; + box.set(10, 20, 10, 20); + ASSERT_EQ(box.write(), "10 20"); + box.set(BOX_TOP, 5, true); + ASSERT_EQ(box.write(), "5 20"); + box.set(BOX_LEFT, 10, true); + ASSERT_EQ(box.write(), "5 10"); + box.set(BOX_LEFT, 5, true); + ASSERT_EQ(box.write(), "5"); + box.set(BOX_BOTTOM, 7, true); + ASSERT_EQ(box.write(), "7"); +} + +// vim: filetype=cpp:expandtab:shiftwidth=4:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/testfiles/src/svg-color-test.cpp b/testfiles/src/svg-color-test.cpp new file mode 100644 index 0000000..c4e9379 --- /dev/null +++ b/testfiles/src/svg-color-test.cpp @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** @file + * Test for SVG colors + *//* + * Authors: see git history + * + * Copyright (C) 2010 Authors + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ +#include "svg/svg-color.h" + +#include +#include + +#include "preferences.h" +#include "svg/svg-icc-color.h" + +static void check_rgb24(unsigned const rgb24) +{ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + char css[8]; + + prefs->setBool("/options/svgoutput/usenamedcolors", false); + sp_svg_write_color(css, sizeof(css), rgb24 << 8); + ASSERT_EQ(sp_svg_read_color(css, 0xff), rgb24 << 8); + + prefs->setBool("/options/svgoutput/usenamedcolors", true); + sp_svg_write_color(css, sizeof(css), rgb24 << 8); + ASSERT_EQ(sp_svg_read_color(css, 0xff), rgb24 << 8); +} + +TEST(SvgColorTest, testWrite) +{ + unsigned const components[] = {0, 0x80, 0xff, 0xc0, 0x77}; + unsigned const nc = G_N_ELEMENTS(components); + for (unsigned i = nc * nc * nc; i--;) { + unsigned tmp = i; + unsigned rgb24 = 0; + for (unsigned c = 0; c < 3; ++c) { + unsigned const component = components[tmp % nc]; + rgb24 = (rgb24 << 8) | component; + tmp /= nc; + } + ASSERT_TRUE(tmp == 0); + check_rgb24(rgb24); + } + + /* And a few completely random ones. */ + for (unsigned i = 500; i--;) { /* Arbitrary number of iterations. */ + unsigned const rgb24 = (std::rand() >> 4) & 0xffffff; + check_rgb24(rgb24); + } +} + +TEST(SvgColorTest, testReadColor) +{ + gchar const *val[] = {"#f0f", "#ff00ff", "rgb(255,0,255)", "fuchsia"}; + size_t const n = sizeof(val) / sizeof(*val); + for (size_t i = 0; i < n; i++) { + gchar const *end = 0; + guint32 result = sp_svg_read_color(val[i], &end, 0x3); + ASSERT_EQ(result, 0xff00ff00); + ASSERT_LT(val[i], end); + } +} + +TEST(SvgColorTest, testIccColor) +{ + struct + { + unsigned numEntries; + bool shouldPass; + char const *name; + char const *str; + } cases[] = { + {1, true, "named", "icc-color(named, 3)"}, + {0, false, "", "foodle"}, + {1, true, "a", "icc-color(a, 3)"}, + {4, true, "named", "icc-color(named, 3, 0, 0.1, 2.5)"}, + {0, false, "", "icc-color(named, 3"}, + {0, false, "", "icc-color(space named, 3)"}, + {0, false, "", "icc-color(tab\tnamed, 3)"}, + {0, false, "", "icc-color(0name, 3)"}, + {0, false, "", "icc-color(-name, 3)"}, + {1, true, "positive", "icc-color(positive, +3)"}, + {1, true, "negative", "icc-color(negative, -3)"}, + {1, true, "positive", "icc-color(positive, +0.1)"}, + {1, true, "negative", "icc-color(negative, -0.1)"}, + {0, false, "", "icc-color(named, value)"}, + {1, true, "hyphen-name", "icc-color(hyphen-name, 1)"}, + {1, true, "under_name", "icc-color(under_name, 1)"}, + }; + + for (size_t i = 0; i < G_N_ELEMENTS(cases); i++) { + SVGICCColor tmp; + char const *str = cases[i].str; + char const *result = nullptr; + + bool parseRet = sp_svg_read_icc_color(str, &result, &tmp); + ASSERT_EQ(parseRet, cases[i].shouldPass) << str; + ASSERT_EQ(tmp.colors.size(), cases[i].numEntries) << str; + if (cases[i].shouldPass) { + ASSERT_STRNE(str, result); + ASSERT_EQ(tmp.colorProfile, cases[i].name) << str; + } else { + ASSERT_STREQ(str, result); + ASSERT_TRUE(tmp.colorProfile.empty()); + } + } +} + +// vim: filetype=cpp:expandtab:shiftwidth=4:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/testfiles/src/svg-extension-test.cpp b/testfiles/src/svg-extension-test.cpp new file mode 100644 index 0000000..4e4d3e5 --- /dev/null +++ b/testfiles/src/svg-extension-test.cpp @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** @file + * SVG Extension test + *//* + * Authors: see git history + * + * Copyright (C) 2020 Authors + * + * Released under GNU GPL version 2 or later, read the file 'COPYING' for more information + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace Inkscape; +using namespace Inkscape::Extension; +using namespace Inkscape::Extension::Internal; + +class SvgExtensionTest : public ::testing::Test { + public: + static std::string create_file(const std::string &filename, const std::string &content) + { + std::stringstream path_builder; + path_builder << "SvgExtensionTest_" << _files.size() << "_" << filename; + std::string path = path_builder.str(); + GError *error = nullptr; + if (!g_file_set_contents(path.c_str(), content.c_str(), content.size(), &error)) { + std::stringstream msg; + msg << "SvgExtensionTest::create_file failed: GError(" << error->domain << ", " << error->code << ", " + << error->message << ")"; + g_error_free(error); + throw std::runtime_error(msg.str()); + } + _files.insert(path); + return path; + } + + static std::set _files; + + protected: + void SetUp() override + { + // setup hidden dependency + Application::create(false); + } + + static void TearDownTestCase() + { + for (auto file : _files) { + if (g_remove(file.c_str())) { + std::cout << "SvgExtensionTest was unable to remove file: " << file << std::endl; + } + } + } +}; + +std::set SvgExtensionTest::_files; + +TEST_F(SvgExtensionTest, openingAsLinkInImageASizelessSvgFileReturnsNull) +{ + std::string sizeless_svg_file = + create_file("sizeless.svg", + ""); + + Svg::init(); + Input *svg_input_extension(dynamic_cast(db.get(SP_MODULE_KEY_INPUT_SVG))); + + Preferences *prefs = Preferences::get(); + prefs->setBool("/options/onimport", true); + prefs->setBool("/dialogs/import/ask_svg", false); + prefs->setString("/dialogs/import/import_mode_svg", "link"); + + ASSERT_EQ(svg_input_extension->open(sizeless_svg_file.c_str()), nullptr); +} + +TEST_F(SvgExtensionTest, hiddenSvg2TextIsSaved) +{ + char const *docString = R"""( + + + + + + + + foo + + + + +)"""; + SPDocument *doc = SPDocument::createNewDocFromMem(docString, static_cast(strlen(docString)), false); + ASSERT_TRUE(doc); + + std::map textMap; + textMap["text1"] = "foo"; + textMap["text2"] = "bar"; + + // otherwise the layout reports a size of 0 + for (const auto& kv : textMap) { + auto textElement = cast(doc->getObjectById(kv.first)); + ASSERT_TRUE(textElement); + textElement->rebuildLayout(); + } + + Inkscape::XML::Document *rdoc = doc->getReprDoc(); + ASSERT_TRUE(rdoc); + + Inkscape::Extension::Internal::insert_text_fallback(rdoc->root(), doc); + + for(const auto& kv : textMap) { + auto textElement = doc->getObjectById(kv.first); + ASSERT_TRUE(textElement); + auto tspanElement = textElement->firstChild(); + ASSERT_TRUE(tspanElement); + auto stringElement = cast(tspanElement->firstChild()); + ASSERT_TRUE(stringElement); + ASSERT_EQ(kv.second, stringElement->string); + } +} diff --git a/testfiles/src/svg-length-test.cpp b/testfiles/src/svg-length-test.cpp new file mode 100644 index 0000000..3db293c --- /dev/null +++ b/testfiles/src/svg-length-test.cpp @@ -0,0 +1,225 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** @file + * Test for SVG colors + *//* + * Authors: see git history + * + * Copyright (C) 2010 Authors + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ +#include "svg/svg-length.h" +#include "svg/svg.h" + +#include +#include +#include + +struct test_t +{ + char const *str; + SVGLength::Unit unit; + float value; + float computed; +}; + +test_t absolute_tests[12] = { + // clang-format off + {"0", SVGLength::NONE, 0 , 0}, + {"1", SVGLength::NONE, 1 , 1}, + {"1.00001", SVGLength::NONE, 1.00001 , 1.00001}, + {"1px", SVGLength::PX , 1 , 1}, + {".1px", SVGLength::PX , 0.1 , 0.1}, + {"100pt", SVGLength::PT , 100 , 400.0/3.0}, + {"1e2pt", SVGLength::PT , 100 , 400.0/3.0}, + {"3pc", SVGLength::PC , 3 , 48}, + {"-3.5pc", SVGLength::PC , -3.5 , -3.5*16.0}, + {"1.2345678mm", SVGLength::MM , 1.2345678, 1.2345678f*96.0/25.4}, // TODO: More precise constants? (a 7 digit constant when the default precision is 8 digits?) + {"123.45678cm", SVGLength::CM , 123.45678 , 123.45678f*96.0/2.54}, // Note that svg_length_read is casting the result from g_ascii_strtod to float. + {"73.162987in", SVGLength::INCH, 73.162987 , 73.162987f*96.0/1.00}}; +test_t relative_tests[3] = { + {"123em", SVGLength::EM, 123, 123. * 7.}, + {"123ex", SVGLength::EX, 123, 123. * 13.}, + {"123%", SVGLength::PERCENT, 1.23, 1.23 * 19.}}; +const char* fail_tests[8] = { + "123 px", + "123e", + "123e+m", + "123ec", + "123pxt", + "--123", + "", + "px"}; +// clang-format on + +TEST(SvgLengthTest, testRead) +{ + for (size_t i = 0; i < G_N_ELEMENTS(absolute_tests); i++) { + SVGLength len; + ASSERT_TRUE( len.read(absolute_tests[i].str)) << absolute_tests[i].str; + ASSERT_EQ( len.unit, absolute_tests[i].unit) << absolute_tests[i].str; + ASSERT_EQ( len.value, absolute_tests[i].value) << absolute_tests[i].str; + ASSERT_EQ( len.computed, absolute_tests[i].computed) << absolute_tests[i].str; + } + for (size_t i = 0; i < G_N_ELEMENTS(relative_tests); i++) { + SVGLength len; + ASSERT_TRUE( len.read(relative_tests[i].str)) << relative_tests[i].str; + len.update(7, 13, 19); + ASSERT_EQ( len.unit, relative_tests[i].unit) << relative_tests[i].str; + ASSERT_EQ( len.value, relative_tests[i].value) << relative_tests[i].str; + ASSERT_EQ( len.computed, relative_tests[i].computed) << relative_tests[i].str; + } + for (size_t i = 0; i < G_N_ELEMENTS(fail_tests); i++) { + SVGLength len; + ASSERT_TRUE( !len.read(fail_tests[i])) << fail_tests[i]; + } +} + +TEST(SvgLengthTest, testReadOrUnset) +{ + for (size_t i = 0; i < G_N_ELEMENTS(absolute_tests); i++) { + SVGLength len; + len.readOrUnset(absolute_tests[i].str); + ASSERT_EQ( len.unit, absolute_tests[i].unit) << absolute_tests[i].str; + ASSERT_EQ( len.value, absolute_tests[i].value) << absolute_tests[i].str; + ASSERT_EQ( len.computed, absolute_tests[i].computed) << absolute_tests[i].str; + } + for (size_t i = 0; i < G_N_ELEMENTS(relative_tests); i++) { + SVGLength len; + len.readOrUnset(relative_tests[i].str); + len.update(7, 13, 19); + ASSERT_EQ( len.unit, relative_tests[i].unit) << relative_tests[i].str; + ASSERT_EQ( len.value, relative_tests[i].value) << relative_tests[i].str; + ASSERT_EQ( len.computed, relative_tests[i].computed) << relative_tests[i].str; + } + for (size_t i = 0; i < G_N_ELEMENTS(fail_tests); i++) { + SVGLength len; + len.readOrUnset(fail_tests[i], SVGLength::INCH, 123, 456); + ASSERT_EQ( len.unit, SVGLength::INCH) << fail_tests[i]; + ASSERT_EQ( len.value, 123) << fail_tests[i]; + ASSERT_EQ( len.computed, 456) << fail_tests[i]; + } +} + +TEST(SvgLengthTest, testReadAbsolute) +{ + for (size_t i = 0; i < G_N_ELEMENTS(absolute_tests); i++) { + SVGLength len; + ASSERT_TRUE( len.readAbsolute(absolute_tests[i].str)) << absolute_tests[i].str; + ASSERT_EQ( len.unit, absolute_tests[i].unit) << absolute_tests[i].str; + ASSERT_EQ( len.value, absolute_tests[i].value) << absolute_tests[i].str; + ASSERT_EQ( len.computed, absolute_tests[i].computed) << absolute_tests[i].str; + } + for (size_t i = 0; i < G_N_ELEMENTS(relative_tests); i++) { + SVGLength len; + ASSERT_TRUE( !len.readAbsolute(relative_tests[i].str)) << relative_tests[i].str; + } + for (size_t i = 0; i < G_N_ELEMENTS(fail_tests); i++) { + SVGLength len; + ASSERT_TRUE( !len.readAbsolute(fail_tests[i])) << fail_tests[i]; + } +} + +TEST(SvgLengthTest, testToFromString) +{ + SVGLength len; + ASSERT_TRUE(len.fromString("10", "mm")); + ASSERT_EQ(len.unit, SVGLength::MM); + ASSERT_EQ(len.value, 10); + ASSERT_EQ(len.toString("mm"), "10mm"); + ASSERT_EQ(len.toString("in"), "0.3937008in"); +} + +struct eq_test_t +{ + char const *a; + char const *b; + bool equal; +}; +eq_test_t eq_tests[4] = { + {"", "", true}, + {"1", "1", true}, + {"10mm", "10mm", true}, + {"20mm", "10mm", false}, +}; + +TEST(SvgLengthTest, testEquality) +{ + for (size_t i = 0; i < G_N_ELEMENTS(eq_tests); i++) { + SVGLength len_a; + SVGLength len_b; + len_a.read(eq_tests[i].a); + len_b.read(eq_tests[i].b); + if (eq_tests[i].equal) { + ASSERT_TRUE(len_a == len_b) << eq_tests[i].a << " == " << eq_tests[i].b; + } else { + ASSERT_TRUE(len_a != len_b) << eq_tests[i].a << " != " << eq_tests[i].b; + } + } +} + +TEST(SvgLengthTest, testEnumMappedToString) +{ + for (int i = (static_cast(SVGLength::NONE) + 1); i <= static_cast(SVGLength::LAST_UNIT); i++) { + SVGLength::Unit target = static_cast(i); + // PX is a special case where we don't have a unit string + if ((target != SVGLength::PX)) { + gchar const *val = sp_svg_length_get_css_units(target); + ASSERT_NE(val, "") << i; + } + } +} + +TEST(SvgLengthTest, testStringsAreValidSVG) +{ + gchar const *valid[] = {"", "em", "ex", "px", "pt", "pc", "cm", "mm", "in", "%"}; + std::set validStrings(valid, valid + G_N_ELEMENTS(valid)); + for (int i = (static_cast(SVGLength::NONE) + 1); i <= static_cast(SVGLength::LAST_UNIT); i++) { + SVGLength::Unit target = static_cast(i); + gchar const *val = sp_svg_length_get_css_units(target); + ASSERT_TRUE( validStrings.find(std::string(val)) != validStrings.end()) << i; + } +} + +TEST(SvgLengthTest, testValidSVGStringsSupported) +{ + // Note that "px" is omitted from the list, as it will be assumed to be so if not explicitly set. + gchar const *valid[] = {"em", "ex", "pt", "pc", "cm", "mm", "in", "%"}; + std::set validStrings(valid, valid + G_N_ELEMENTS(valid)); + for (int i = (static_cast(SVGLength::NONE) + 1); i <= static_cast(SVGLength::LAST_UNIT); i++) { + SVGLength::Unit target = static_cast(i); + gchar const *val = sp_svg_length_get_css_units(target); + std::set::iterator iter = validStrings.find(std::string(val)); + if (iter != validStrings.end()) { + validStrings.erase(iter); + } + } + ASSERT_EQ(validStrings.size(), 0u) << validStrings.size(); +} + +TEST(SvgLengthTest, testPlaces) +{ + struct testd_t + { + char const *str; + double val; + int prec; + int minexp; + }; + + testd_t const precTests[] = { + {"760", 761.92918978947023, 2, -8}, + {"761.9", 761.92918978947023, 4, -8}, + }; + + for (size_t i = 0; i < G_N_ELEMENTS(precTests); i++) { + std::string buf; + buf.append(sp_svg_number_write_de(precTests[i].val, precTests[i].prec, precTests[i].minexp)); + unsigned int retval = buf.length(); + ASSERT_EQ( retval, strlen(precTests[i].str)) << "Number of chars written"; + ASSERT_EQ( std::string(buf), std::string(precTests[i].str)) << "Numeric string written"; + } +} + +// TODO: More tests + +// vim: filetype=cpp:expandtab:shiftwidth=4:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/testfiles/src/svg-path-geom-test.cpp b/testfiles/src/svg-path-geom-test.cpp new file mode 100644 index 0000000..4021120 --- /dev/null +++ b/testfiles/src/svg-path-geom-test.cpp @@ -0,0 +1,507 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** @file + * Test for SVG colors + *//* + * Authors: see git history + * + * Copyright (C) 2010 Authors + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ +#include <2geom/coord.h> +#include <2geom/curves.h> +#include <2geom/pathvector.h> +#include +#include +#include + +#include "preferences.h" +#include "svg/svg.h" +#include "helper/geom.h" + +class SvgPathGeomTest : public ::testing::Test +{ +public: + std::vector rectanglesAbsoluteClosed = {"M 1,2 L 4,2 L 4,8 L 1,8 z", "M 1,2 4,2 4,8 1,8 z", + "M 1,2 H 4 V 8 H 1 z"}; + std::vector rectanglesRelativeClosed = {"m 1,2 l 3,0 l 0,6 l -3,0 z", "m 1,2 3,0 0,6 -3,0 z", + "m 1,2 h 3 v 6 h -3 z"}; + std::vector rectanglesAbsoluteOpen = {"M 1,2 L 4,2 L 4,8 L 1,8 L 1,2", "M 1,2 4,2 4,8 1,8 1,2", + "M 1,2 H 4 V 8 H 1 V 2"}; + std::vector rectanglesRelativeOpen = {"m 1,2 l 3,0 l 0,6 l -3,0 l 0,-6", "m 1,2 3,0 0,6 -3,0 0,-6", + "m 1,2 h 3 v 6 h -3 v -6"}; + std::vector rectanglesAbsoluteClosed2 = {"M 1,2 L 4,2 L 4,8 L 1,8 L 1,2 z", "M 1,2 4,2 4,8 1,8 1,2 z", + "M 1,2 H 4 V 8 H 1 V 2 z"}; + std::vector rectanglesRelativeClosed2{"m 1,2 l 3,0 l 0,6 l -3,0 l 0,-6 z", "m 1,2 3,0 0,6 -3,0 0,-6 z", + "m 1,2 h 3 v 6 h -3 v -6 z"}; + Geom::PathVector rectanglepvopen; + Geom::PathVector rectanglepvclosed; + Geom::PathVector rectanglepvclosed2; + + void SetUp() override + { + rectanglepvopen.clear(); + rectanglepvopen.push_back(Geom::Path(Geom::Point(1, 2))); + rectanglepvopen.back().append(Geom::LineSegment(Geom::Point(1, 2), Geom::Point(4, 2))); + rectanglepvopen.back().append(Geom::LineSegment(Geom::Point(4, 2), Geom::Point(4, 8))); + rectanglepvopen.back().append(Geom::LineSegment(Geom::Point(4, 8), Geom::Point(1, 8))); + rectanglepvopen.back().append(Geom::LineSegment(Geom::Point(1, 8), Geom::Point(1, 2))); + rectanglepvclosed.clear(); + rectanglepvclosed.push_back(Geom::Path(Geom::Point(1, 2))); + rectanglepvclosed.back().append(Geom::LineSegment(Geom::Point(1, 2), Geom::Point(4, 2))); + rectanglepvclosed.back().append(Geom::LineSegment(Geom::Point(4, 2), Geom::Point(4, 8))); + rectanglepvclosed.back().append(Geom::LineSegment(Geom::Point(4, 8), Geom::Point(1, 8))); + rectanglepvclosed.back().close(); + rectanglepvclosed2.clear(); + rectanglepvclosed2.push_back(Geom::Path(Geom::Point(1, 2))); + rectanglepvclosed2.back().append(Geom::LineSegment(Geom::Point(1, 2), Geom::Point(4, 2))); + rectanglepvclosed2.back().append(Geom::LineSegment(Geom::Point(4, 2), Geom::Point(4, 8))); + rectanglepvclosed2.back().append(Geom::LineSegment(Geom::Point(4, 8), Geom::Point(1, 8))); + rectanglepvclosed2.back().append(Geom::LineSegment(Geom::Point(1, 8), Geom::Point(1, 2))); + rectanglepvclosed2.back().close(); + } + + bool bpathEqual(Geom::PathVector const &a, Geom::PathVector const &b, double eps = 1e-16) + { + if (a.size() != b.size()) { + printf("PathVectors not the same size: %u != %u", static_cast(a.size()), + static_cast(b.size())); + return false; + } + for (size_t i = 0; i < a.size(); i++) { + Geom::Path const &pa = a[i]; + Geom::Path const &pb = b[i]; + if (pa.closed() && !pb.closed()) { + printf("Left subpath is closed, right subpath is open. Subpath: %u", static_cast(i)); + return false; + } + if (!pa.closed() && pb.closed()) { + printf("Right subpath is closed, left subpath is open. Subpath: %u", static_cast(i)); + return false; + } + if (pa.size() != pb.size()) { + printf("Not the same number of segments: %u != %u, subpath: %u", static_cast(pa.size()), + static_cast(pb.size()), static_cast(i)); + return false; + } + for (size_t j = 0; j < pa.size(); j++) { + Geom::Curve const *ca = &pa[j]; + Geom::Curve const *cb = &pb[j]; + if (typeid(*ca) == typeid(*cb)) { + if (Geom::LineSegment const *la = dynamic_cast(ca)) { + Geom::LineSegment const *lb = dynamic_cast(cb); + if (!Geom::are_near((*la)[0], (*lb)[0], eps)) { + printf("Different start of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", + (*la)[0][Geom::X], (*la)[0][Geom::Y], (*lb)[0][Geom::X], (*lb)[0][Geom::Y], + static_cast(i), static_cast(j)); + return false; + } + if (!Geom::are_near((*la)[1], (*lb)[1], eps)) { + printf("Different end of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", + (*la)[1][Geom::X], (*la)[1][Geom::Y], (*lb)[1][Geom::X], (*lb)[1][Geom::Y], + static_cast(i), static_cast(j)); + return false; + } + } else if (Geom::CubicBezier const *la = dynamic_cast(ca)) { + Geom::CubicBezier const *lb = dynamic_cast(cb); + if (!Geom::are_near((*la)[0], (*lb)[0], eps)) { + printf("Different start of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", + (*la)[0][Geom::X], (*la)[0][Geom::Y], (*lb)[0][Geom::X], (*lb)[0][Geom::Y], + static_cast(i), static_cast(j)); + return false; + } + if (!Geom::are_near((*la)[1], (*lb)[1], eps)) { + printf("Different 1st control point: (%g,%g) != (%g,%g), subpath: %u, segment: %u", + (*la)[1][Geom::X], (*la)[1][Geom::Y], (*lb)[1][Geom::X], (*lb)[1][Geom::Y], + static_cast(i), static_cast(j)); + return false; + } + if (!Geom::are_near((*la)[2], (*lb)[2], eps)) { + printf("Different 2nd control point: (%g,%g) != (%g,%g), subpath: %u, segment: %u", + (*la)[2][Geom::X], (*la)[2][Geom::Y], (*lb)[2][Geom::X], (*lb)[2][Geom::Y], + static_cast(i), static_cast(j)); + return false; + } + if (!Geom::are_near((*la)[3], (*lb)[3], eps)) { + printf("Different end of segment: (%g,%g) != (%g,%g), subpath: %u, segment: %u", + (*la)[3][Geom::X], (*la)[3][Geom::Y], (*lb)[3][Geom::X], (*lb)[3][Geom::Y], + static_cast(i), static_cast(j)); + return false; + } + } else { + printf("Unknown curve type: %s, subpath: %u, segment: %u", typeid(*ca).name(), + static_cast(i), static_cast(j)); + return false; + } + } else // not same type + { + printf("Different curve types: %s != %s, subpath: %u, segment: %u", typeid(*ca).name(), + typeid(*cb).name(), static_cast(i), static_cast(j)); + return false; + } + } + } + return true; + } +}; + +TEST_F(SvgPathGeomTest, testReadRectanglesAbsoluteClosed) +{ + for (size_t i = 0; i < rectanglesAbsoluteClosed.size(); i++) { + Geom::PathVector pv = sp_svg_read_pathv(rectanglesAbsoluteClosed[i].c_str()); + EXPECT_TRUE(bpathEqual(pv, rectanglepvclosed)) << rectanglesAbsoluteClosed[i].c_str(); + } +} + +TEST_F(SvgPathGeomTest, testReadRectanglesRelativeClosed) +{ + for (size_t i = 0; i < rectanglesRelativeClosed.size(); i++) { + Geom::PathVector pv = sp_svg_read_pathv(rectanglesRelativeClosed[i].c_str()); + ASSERT_TRUE(bpathEqual(pv, rectanglepvclosed)) << rectanglesRelativeClosed[i].c_str(); + } +} + +TEST_F(SvgPathGeomTest, testReadRectanglesAbsoluteOpen) +{ + for (size_t i = 0; i < rectanglesAbsoluteOpen.size(); i++) { + Geom::PathVector pv = sp_svg_read_pathv(rectanglesAbsoluteOpen[i].c_str()); + ASSERT_TRUE(bpathEqual(pv, rectanglepvopen)) << rectanglesAbsoluteOpen[i].c_str(); + } +} + +TEST_F(SvgPathGeomTest, testReadRectanglesRelativeOpen) +{ + for (size_t i = 0; i < rectanglesRelativeOpen.size(); i++) { + Geom::PathVector pv = sp_svg_read_pathv(rectanglesRelativeOpen[i].c_str()); + ASSERT_TRUE(bpathEqual(pv, rectanglepvopen)) << rectanglesRelativeOpen[i].c_str(); + } +} + +TEST_F(SvgPathGeomTest, testReadRectanglesAbsoluteClosed2) +{ + for (size_t i = 0; i < rectanglesAbsoluteClosed2.size(); i++) { + Geom::PathVector pv = sp_svg_read_pathv(rectanglesAbsoluteClosed2[i].c_str()); + ASSERT_TRUE(bpathEqual(pv, rectanglepvclosed2)) << rectanglesAbsoluteClosed2[i].c_str(); + } +} + +TEST_F(SvgPathGeomTest, testReadRectanglesRelativeClosed2) +{ + for (size_t i = 0; i < rectanglesRelativeClosed2.size(); i++) { + Geom::PathVector pv = sp_svg_read_pathv(rectanglesRelativeClosed2[i].c_str()); + ASSERT_TRUE(bpathEqual(pv, rectanglepvclosed2)) << rectanglesRelativeClosed2[i].c_str(); + } +} + +TEST_F(SvgPathGeomTest, testReadConcatenatedPaths) +{ + // Note that finalPoint doesn't actually return the final point of the path, just the last given point... (but since + // this might be intentional and we're not testing lib2geom here, we just specify the final point explicitly + Geom::PathVector pv_good; + pv_good.push_back(rectanglepvclosed.back()); + pv_good.push_back(rectanglepvopen.back() * Geom::Translate(1, 2) /* * Geom::Translate(pv_good[0].finalPoint())*/); + pv_good.push_back(rectanglepvclosed.back() * Geom::Translate(2, 4) /* *Geom::Translate(pv_good[1].finalPoint())*/); + pv_good.push_back(rectanglepvopen.back()); + pv_good[0].close(); + pv_good[1].close(false); + pv_good[2].close(); + pv_good[3].close(false); + std::string path_str = rectanglesAbsoluteClosed[0] + rectanglesRelativeOpen[0] + rectanglesRelativeClosed[0] + + rectanglesAbsoluteOpen[0]; + Geom::PathVector pv = sp_svg_read_pathv(path_str.c_str()); + ASSERT_TRUE(bpathEqual(pv, pv_good)); +} + +TEST_F(SvgPathGeomTest, testReadZeroLengthSubpaths) +{ + // Per the SVG 1.1 specification (section F5) zero-length subpaths are relevant + Geom::PathVector pv_good; + pv_good.push_back(Geom::Path(Geom::Point(0, 0))); + pv_good.push_back(Geom::Path(Geom::Point(1, 1))); + pv_good.back().append(Geom::LineSegment(Geom::Point(1, 1), Geom::Point(2, 2))); + pv_good.push_back(Geom::Path(Geom::Point(3, 3))); + pv_good.back().close(); + pv_good.push_back(Geom::Path(Geom::Point(4, 4))); + pv_good.back().append(Geom::LineSegment(Geom::Point(4, 4), Geom::Point(5, 5))); + pv_good.back().close(); + pv_good.push_back(Geom::Path(Geom::Point(6, 6))); + { // Test absolute version + char const *path_str = "M 0,0 M 1,1 L 2,2 M 3,3 z M 4,4 L 5,5 z M 6,6"; + Geom::PathVector pv = sp_svg_read_pathv(path_str); + ASSERT_TRUE(bpathEqual(pv, pv_good)) << path_str; + } + { // Test relative version + char const *path_str = "m 0,0 m 1,1 l 1,1 m 1,1 z m 1,1 l 1,1 z m 2,2"; + Geom::PathVector pv = sp_svg_read_pathv(path_str); + ASSERT_TRUE(bpathEqual(pv, pv_good)) << path_str; + } +} + +TEST_F(SvgPathGeomTest, testReadImplicitMoveto) +{ + g_warning("Currently lib2geom (/libnr) has no way of specifying the difference between 'M 0,0 ... z M 0,0 L 1,0' " + "and 'M 0,0 ... z L 1,0', the SVG specification does state that these should be handled differently with " + "respect to markers however, see the description of the 'orient' attribute of the 'marker' element."); + Geom::PathVector pv_good; + pv_good.push_back(Geom::Path(Geom::Point(1, 1))); + pv_good.back().append(Geom::LineSegment(Geom::Point(1, 1), Geom::Point(2, 2))); + pv_good.back().close(); + pv_good.push_back(Geom::Path(Geom::Point(1, 1))); + pv_good.back().append(Geom::LineSegment(Geom::Point(1, 1), Geom::Point(3, 3))); + pv_good.back().close(); + { // Test absolute version + char const *path_str = "M 1,1 L 2,2 z L 3,3 z"; + Geom::PathVector pv = sp_svg_read_pathv(path_str); + ASSERT_TRUE(bpathEqual(pv, pv_good)) << path_str; + } + { // Test relative version + char const *path_str = "M 1,1 l 1,1 z l 2,2 z"; + Geom::PathVector pv = sp_svg_read_pathv(path_str); + ASSERT_TRUE(bpathEqual(pv, pv_good)) << path_str; + } +} + +TEST_F(SvgPathGeomTest, testReadFloatingPoint) +{ + Geom::PathVector pv_good1; + pv_good1.push_back(Geom::Path(Geom::Point(.01, .02))); + pv_good1.back().append(Geom::LineSegment(Geom::Point(.01, .02), Geom::Point(.04, .02))); + pv_good1.back().append(Geom::LineSegment(Geom::Point(.04, .02), Geom::Point(1.5, 1.6))); + pv_good1.back().append(Geom::LineSegment(Geom::Point(1.5, 1.6), Geom::Point(.01, .08))); + pv_good1.back().append(Geom::LineSegment(Geom::Point(.01, .08), Geom::Point(.01, .02))); + pv_good1.back().close(); + { // Test decimals + char const *path_str = "M .01,.02 L.04.02 L1.5,1.6L0.01,0.08 .01.02 z"; + Geom::PathVector pv = sp_svg_read_pathv(path_str); + ASSERT_TRUE(bpathEqual(pv, pv_good1)) << path_str; + } + Geom::PathVector pv_good2; + pv_good2.push_back(Geom::Path(Geom::Point(.01, .02))); + pv_good2.back().append(Geom::LineSegment(Geom::Point(.01, .02), Geom::Point(.04, .02))); + pv_good2.back().append(Geom::LineSegment(Geom::Point(.04, .02), Geom::Point(1.5, 1.6))); + pv_good2.back().append(Geom::LineSegment(Geom::Point(1.5, 1.6), Geom::Point(.01, .08))); + pv_good2.back().close(); + { // Test exponent + char const *path_str = "M 1e-2,.2e-1 L 0.004e1,0.0002e+2 L0150E-2,1.6e0L1.0e-2,80e-3 z"; + Geom::PathVector pv = sp_svg_read_pathv(path_str); + ASSERT_TRUE(bpathEqual(pv, pv_good2)) << path_str; + } +} + +TEST_F(SvgPathGeomTest, testReadImplicitSeparation) +{ + // Coordinates need not be separated by whitespace if they can still be read unambiguously + Geom::PathVector pv_good; + pv_good.push_back(Geom::Path(Geom::Point(.1, .2))); + pv_good.back().append(Geom::LineSegment(Geom::Point(.1, .2), Geom::Point(.4, .2))); + pv_good.back().append(Geom::LineSegment(Geom::Point(.4, .2), Geom::Point(.4, .8))); + pv_good.back().append(Geom::LineSegment(Geom::Point(.4, .8), Geom::Point(.1, .8))); + pv_good.back().close(); + { // Test absolute + char const *path_str = "M .1.2+0.4.2e0.4e0+8e-1.1.8 z"; + Geom::PathVector pv = sp_svg_read_pathv(path_str); + ASSERT_TRUE(bpathEqual(pv, pv_good)) << path_str; + } + { // Test relative + char const *path_str = "m .1.2+0.3.0e0.0e0+6e-1-.3.0 z"; + Geom::PathVector pv = sp_svg_read_pathv(path_str); + ASSERT_TRUE(bpathEqual(pv, pv_good)) << path_str; + } +} + +TEST_F(SvgPathGeomTest, testReadErrorMisplacedCharacter) +{ + + char const *path_str; + Geom::PathVector pv; + // Comma in the wrong place (commas may only appear between parameters) + path_str = "M 1,2 4,2 4,8 1,8 z , m 13,15"; + pv = sp_svg_read_pathv(path_str); + ASSERT_TRUE(bpathEqual(pv, rectanglepvclosed)) << path_str; + // Comma in the wrong place (commas may only appear between parameters) + path_str = "M 1,2 4,2 4,8 1,8 z m,13,15"; + pv = sp_svg_read_pathv(path_str); + ASSERT_TRUE(bpathEqual(pv, rectanglepvclosed)) << path_str; + // Period in the wrong place (no numbers after a 'z') + path_str = "M 1,2 4,2 4,8 1,8 z . m 13,15"; + pv = sp_svg_read_pathv(path_str); + ASSERT_TRUE(bpathEqual(pv, rectanglepvclosed)) << path_str; + // Sign in the wrong place (no numbers after a 'z') + path_str = "M 1,2 4,2 4,8 1,8 z + - m 13,15"; + pv = sp_svg_read_pathv(path_str); + ASSERT_TRUE(bpathEqual(pv, rectanglepvclosed)) << path_str; + // Digit in the wrong place (no numbers after a 'z') + path_str = "M 1,2 4,2 4,8 1,8 z 9809 m 13,15"; + pv = sp_svg_read_pathv(path_str); + ASSERT_TRUE(bpathEqual(pv, rectanglepvclosed)) << path_str; + // Digit in the wrong place (no numbers after a 'z') + path_str = "M 1,2 4,2 4,8 1,8 z 9809 876 m 13,15"; + pv = sp_svg_read_pathv(path_str); + ASSERT_TRUE(bpathEqual(pv, rectanglepvclosed)) << path_str; +} +/*FAIL ?? +TEST_F(SvgPathGeomTest, testReadErrorUnrecognizedCharacter) +{ + char const *path_str; + Geom::PathVector pv; + // Unrecognized character + path_str = "M 1,2 4,2 4,8 1,8 z&m 13,15"; + pv = sp_svg_read_pathv(path_str); + ASSERT_TRUE(bpathEqual(pv, rectanglepvclosed)) << path_str; + // Unrecognized character + path_str = "M 1,2 4,2 4,8 1,8 z m &13,15"; + pv = sp_svg_read_pathv(path_str); + ASSERT_TRUE(bpathEqual(pv, rectanglepvclosed)) << path_str; +} + +TEST_F(SvgPathGeomTest, testReadErrorTypo) +{ + char const *path_str; + Geom::PathVector pv; + // Typo + path_str = "M 1,2 4,2 4,8 1,8 z j 13,15"; + pv = sp_svg_read_pathv(path_str); + ASSERT_TRUE(bpathEqual(pv, rectanglepvclosed)) << path_str; + + // Typo + path_str = "M 1,2 4,2 4,8 1,8 L 1,2 x m 13,15"; + pv = sp_svg_read_pathv(path_str); + ASSERT_TRUE(bpathEqual(pv, rectanglepvopen)) << path_str; +} +*/ +TEST_F(SvgPathGeomTest, testReadErrorIllformedNumbers) +{ + char const *path_str; + Geom::PathVector pv; + // Double exponent + path_str = "M 1,2 4,2 4,8 1,8 z m 13e4e5,15"; + pv = sp_svg_read_pathv(path_str); + ASSERT_TRUE(bpathEqual(pv, rectanglepvclosed)) << path_str; + // Double sign + path_str = "M 1,2 4,2 4,8 1,8 z m +-13,15"; + pv = sp_svg_read_pathv(path_str); + ASSERT_TRUE(bpathEqual(pv, rectanglepvclosed)) << path_str; + // Double sign + path_str = "M 1,2 4,2 4,8 1,8 z m 13e+-12,15"; + pv = sp_svg_read_pathv(path_str); + ASSERT_TRUE(bpathEqual(pv, rectanglepvclosed)) << path_str; + // No digit + path_str = "M 1,2 4,2 4,8 1,8 z m .e12,15"; + pv = sp_svg_read_pathv(path_str); + ASSERT_TRUE(bpathEqual(pv, rectanglepvclosed)) << path_str; + // No digit + path_str = "M 1,2 4,2 4,8 1,8 z m .,15"; + pv = sp_svg_read_pathv(path_str); + ASSERT_TRUE(bpathEqual(pv, rectanglepvclosed)) << path_str; + // No digit + path_str = "M 1,2 4,2 4,8 1,8 z m +,15"; + pv = sp_svg_read_pathv(path_str); + ASSERT_TRUE(bpathEqual(pv, rectanglepvclosed)) << path_str; + // No digit + path_str = "M 1,2 4,2 4,8 1,8 z m +.e+,15"; + pv = sp_svg_read_pathv(path_str); + ASSERT_TRUE(bpathEqual(pv, rectanglepvclosed)) << path_str; +} + +TEST_F(SvgPathGeomTest, testReadErrorJunk) +{ + char const *path_str; + Geom::PathVector pv; + // Junk + path_str = "M 1,2 4,2 4,8 1,8 z j 357 hkjh.,34e34 90ih6kj4 h5k6vlh4N.,6,45wikuyi3yere..3487 m 13,23"; + pv = sp_svg_read_pathv(path_str); + ASSERT_TRUE(bpathEqual(pv, rectanglepvclosed)) << path_str; +} +/* FAIL ??? +TEST_F(SvgPathGeomTest, testReadErrorStopReading) +{ + char const *path_str; + Geom::PathVector pv; + // Unrecognized parameter + path_str = "M 1,2 4,2 4,8 1,8 z m #$%,23,34"; + pv = sp_svg_read_pathv(path_str); + ASSERT_TRUE(bpathEqual(pv, rectanglepvclosed)) << path_str; + // Invalid parameter + path_str = "M 1,2 4,2 4,8 1,8 z m #$%,23,34"; + pv = sp_svg_read_pathv(path_str); + ASSERT_TRUE(bpathEqual(pv, rectanglepvclosed)) << path_str; + // Illformed parameter + path_str = "M 1,2 4,2 4,8 1,8 z m +-12,23,34"; + pv = sp_svg_read_pathv(path_str); + ASSERT_TRUE(bpathEqual(pv, rectanglepvclosed)) << path_str; + + // "Third" parameter + path_str = "M 1,2 4,2 4,8 1,8 1,2,3 M 12,23"; + pv = sp_svg_read_pathv(path_str); + ASSERT_TRUE(bpathEqual(pv, rectanglepvopen)) << path_str; +} +*/ + +TEST_F(SvgPathGeomTest, testRoundTrip) +{ + // This is the easiest way to (also) test writing path data, as a path can be written in more than one way. + Geom::PathVector pv; + Geom::PathVector new_pv; + std::string org_path_str; + std::string path_str; + // Rectangle (closed) + org_path_str = rectanglesAbsoluteClosed[0]; + pv = sp_svg_read_pathv(org_path_str.c_str()); + path_str = sp_svg_write_path(pv); + new_pv = sp_svg_read_pathv(path_str.c_str()); + ASSERT_TRUE(bpathEqual(pv, new_pv)) << org_path_str.c_str(); + // Rectangle (open) + org_path_str = rectanglesAbsoluteOpen[0]; + pv = sp_svg_read_pathv(org_path_str.c_str()); + path_str = sp_svg_write_path(pv); + new_pv = sp_svg_read_pathv(path_str.c_str()); + ASSERT_TRUE(bpathEqual(pv, new_pv)) << org_path_str.c_str(); + // Concatenated rectangles + org_path_str = rectanglesAbsoluteClosed[0] + rectanglesRelativeOpen[0] + rectanglesRelativeClosed[0] + + rectanglesAbsoluteOpen[0]; + pv = sp_svg_read_pathv(org_path_str.c_str()); + path_str = sp_svg_write_path(pv); + new_pv = sp_svg_read_pathv(path_str.c_str()); + ASSERT_TRUE(bpathEqual(pv, new_pv)) << org_path_str.c_str(); + // Zero-length subpaths + org_path_str = "M 0,0 M 1,1 L 2,2 M 3,3 z M 4,4 L 5,5 z M 6,6"; + pv = sp_svg_read_pathv(org_path_str.c_str()); + path_str = sp_svg_write_path(pv); + new_pv = sp_svg_read_pathv(path_str.c_str()); + ASSERT_TRUE(bpathEqual(pv, new_pv)) << org_path_str.c_str(); + // Floating-point + org_path_str = "M .01,.02 L 0.04,0.02 L.04,.08L0.01,0.08 z" + "M 1e-2,.2e-1 L 0.004e1,0.0002e+2 L04E-2,.08e0L1.0e-2,80e-3 z"; + pv = sp_svg_read_pathv(org_path_str.c_str()); + path_str = sp_svg_write_path(pv); + new_pv = sp_svg_read_pathv(path_str.c_str()); + ASSERT_TRUE(bpathEqual(pv, new_pv, 1e-17)) << org_path_str.c_str(); +} + +TEST(PathVectorToBeziersTest, random) +{ + // Evil test will crash if not protected + Geom::PathVector pathv = sp_svg_read_pathv("M349 683 A170 170 0 1 0 349.00000000000006 683"); + pathv_to_linear_and_cubic_beziers(pathv); +} + +/* + * Please do not change my prefs or put them back after :( + * also, fails. + +TEST_F(SvgPathGeomTest, testMinexpPrecision) +{ + Geom::PathVector pv; + char *path_str; + // Default values + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + prefs->setBool("/options/svgoutput/allowrelativecoordinates", true); + prefs->setBool("/options/svgoutput/forcerepeatcommands", false); + prefs->setInt("/options/svgoutput/numericprecision", 8); + prefs->setInt("/options/svgoutput/minimumexponent", -8); + pv = sp_svg_read_pathv("M 123456781,1.23456781e-8 L 123456782,1.23456782e-8 L 123456785,1.23456785e-8 L +10123456400,1.23456785e-8 L 123456789,1.23456789e-8 L 123456789,101.234564e-8 L 123456789,1.23456789e-8"); path_str = +sp_svg_write_path(pv); ASSERT_FALSE( strcmp("m 123456780,1.2345678e-8 0,0 10,1e-15 9999999210,0 -9999999210,0 +0,9.99999921e-7 0,-9.99999921e-7" , path_str )); g_free(path_str); +}*/ + +// vim: filetype=cpp:expandtab:shiftwidth=4:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/testfiles/src/svg-stringstream-test.cpp b/testfiles/src/svg-stringstream-test.cpp new file mode 100644 index 0000000..636b018 --- /dev/null +++ b/testfiles/src/svg-stringstream-test.cpp @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** + * @file + * Test CSSOStringStream and SVGOStringStream + */ +/* + * Authors: + * Thomas Holder + * + * Copyright (C) 2019 Authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include "2geom/point.h" +#include "svg/css-ostringstream.h" +#include "svg/stringstream.h" + +#include "gtest/gtest.h" +#include + +template +static void assert_tostring_eq(T value, const char *expected) +{ + S os; + + // default of /options/svgoutput/numericprecision + os.precision(8); + + os << value; + ASSERT_EQ(os.str(), expected); +} + +#define TEST_STRING "Hello & " + +template +void test_tostring() +{ + assert_tostring_eq('A', "A"); + assert_tostring_eq('A', "A"); + assert_tostring_eq('A', "A"); + + assert_tostring_eq(0x7FFF, "32767"); + assert_tostring_eq(-30000, "-30000"); + assert_tostring_eq(0xFFFFu, "65535"); + assert_tostring_eq(0x7FFFFFFF, "2147483647"); + assert_tostring_eq(-2000000000, "-2000000000"); + assert_tostring_eq(0xFFFFFFFFu, "4294967295"); + + // long is 32bit on Windows, 64bit on Linux + assert_tostring_eq(0x7FFFFFFFL, "2147483647"); + assert_tostring_eq(-2000000000L, "-2000000000"); + assert_tostring_eq(0xFFFFFFFFuL, "4294967295"); + + assert_tostring_eq((char const *)TEST_STRING, TEST_STRING); + assert_tostring_eq((signed char const *)TEST_STRING, TEST_STRING); + assert_tostring_eq((unsigned char const *)TEST_STRING, TEST_STRING); + assert_tostring_eq(TEST_STRING, TEST_STRING); + assert_tostring_eq(TEST_STRING, TEST_STRING); +} + +TEST(CSSOStringStreamTest, tostring) +{ + using S = Inkscape::CSSOStringStream; + + test_tostring(); + + // float has 6 significant digits + assert_tostring_eq(0.0, "0"); + assert_tostring_eq(4.5, "4.5"); + assert_tostring_eq(-4.0, "-4"); + assert_tostring_eq(0.001, "0.001"); + assert_tostring_eq(0.00123456, "0.00123456"); + assert_tostring_eq(-0.00123456, "-0.00123456"); + assert_tostring_eq(-1234560.0, "-1234560"); + + // double has 15 significant digits + assert_tostring_eq(0.0, "0"); + assert_tostring_eq(4.5, "4.5"); + assert_tostring_eq(-4.0, "-4"); + assert_tostring_eq(0.001, "0.001"); + + // 9 significant digits + assert_tostring_eq(1.23456789, "1.23456789"); + assert_tostring_eq(-1.23456789, "-1.23456789"); + assert_tostring_eq(12345678.9, "12345678.9"); + assert_tostring_eq(-12345678.9, "-12345678.9"); + + assert_tostring_eq(1.234e-12, "0"); + assert_tostring_eq(3e9, "3000000000"); + assert_tostring_eq(-3.5e9, "-3500000000"); +} + +TEST(SVGOStringStreamTest, tostring) +{ + using S = Inkscape::SVGOStringStream; + + test_tostring(); + + assert_tostring_eq(Geom::Point(12, 3.4), "12,3.4"); + + // float has 6 significant digits + assert_tostring_eq(0.0, "0"); + assert_tostring_eq(4.5, "4.5"); + assert_tostring_eq(-4.0, "-4"); + assert_tostring_eq(0.001, "0.001"); + assert_tostring_eq(0.00123456, "0.00123456"); + assert_tostring_eq(-0.00123456, "-0.00123456"); + assert_tostring_eq(-1234560.0, "-1234560"); + + // double has 15 significant digits + assert_tostring_eq(0.0, "0"); + assert_tostring_eq(4.5, "4.5"); + assert_tostring_eq(-4.0, "-4"); + assert_tostring_eq(0.001, "0.001"); + + // 8 significant digits + assert_tostring_eq(1.23456789, "1.2345679"); + assert_tostring_eq(-1.23456789, "-1.2345679"); + assert_tostring_eq(12345678.9, "12345679"); + assert_tostring_eq(-12345678.9, "-12345679"); + + assert_tostring_eq(1.234e-12, "1.234e-12"); + assert_tostring_eq(3e9, "3e+09"); + assert_tostring_eq(-3.5e9, "-3.5e+09"); +} + +template +void test_concat() +{ + S s; + s << "hello, "; + s << -53.5; + ASSERT_EQ(s.str(), std::string("hello, -53.5")); +} + +TEST(CSSOStringStreamTest, concat) +{ // + test_concat(); +} + +TEST(SVGOStringStreamTest, concat) +{ // + test_concat(); +} + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/testfiles/src/uri-test.cpp b/testfiles/src/uri-test.cpp new file mode 100644 index 0000000..91af2e7 --- /dev/null +++ b/testfiles/src/uri-test.cpp @@ -0,0 +1,304 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** + * @file + * Test Inkscape::URI + */ +/* + * Authors: + * Thomas Holder + * + * Copyright (C) 2018 Authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include "object/uri.h" +#include "gtest/gtest.h" + +using Inkscape::URI; + +#define BASE64_HELLO_WORLD_P1 "SGVsbG8g" +#define BASE64_HELLO_WORLD_P2 "V29ybGQ=" +#define DATA_BASE64_HEADER "data:text/plain;charset=utf-8;base64," +char const *DATA_BASE64_HELLO_WORLD = DATA_BASE64_HEADER BASE64_HELLO_WORLD_P1 BASE64_HELLO_WORLD_P2; +char const *DATA_BASE64_HELLO_WORLD_WRAPPED = DATA_BASE64_HEADER BASE64_HELLO_WORLD_P1 "\n" BASE64_HELLO_WORLD_P2; + +char const *win_url_unc = "file://laptop/My%20Documents/FileSchemeURIs.doc"; +char const *win_url_local = "file:///C:/Documents%20and%20Settings/davris/FileSchemeURIs.doc"; +char const *win_filename_local = "C:\\Documents and Settings\\davris\\FileSchemeURIs.doc"; + +TEST(UriTest, Malformed) +{ + ASSERT_ANY_THROW(URI(nullptr)); + ASSERT_ANY_THROW(URI("nonhex-%XX")); +} + +TEST(UriTest, GetPath) +{ + ASSERT_STREQ(URI().getPath(), nullptr); + ASSERT_STREQ(URI("foo.svg").getPath(), "foo.svg"); + ASSERT_STREQ(URI("foo.svg#bar").getPath(), "foo.svg"); + ASSERT_STREQ(URI("#bar").getPath(), nullptr); + ASSERT_STREQ(URI("scheme://host").getPath(), nullptr); + ASSERT_STREQ(URI("scheme://host/path").getPath(), "/path"); + ASSERT_STREQ(URI("scheme://host/path?query").getPath(), "/path"); + ASSERT_STREQ(URI("scheme:/path").getPath(), "/path"); +} + +TEST(UriTest, FromDir) +{ +#ifdef _WIN32 + ASSERT_EQ(URI::from_dirname("C:\\tmp").str(), "file:///C:/tmp/"); + ASSERT_EQ(URI::from_dirname("C:\\").str(), "file:///C:/"); + ASSERT_EQ(URI::from_href_and_basedir("uri.svg", "C:\\tmp").str(), "file:///C:/tmp/uri.svg"); +#else + ASSERT_EQ(URI::from_dirname("/").str(), "file:///"); + ASSERT_EQ(URI::from_dirname("/tmp").str(), "file:///tmp/"); + ASSERT_EQ(URI::from_href_and_basedir("uri.svg", "/tmp").str(), "file:///tmp/uri.svg"); +#endif +} + +TEST(UriTest, Str) +{ + ASSERT_EQ(URI().str(), ""); + ASSERT_EQ(URI("").str(), ""); + ASSERT_EQ(URI("", "http://a/b").str(), "http://a/b"); + + ASSERT_EQ(URI("uri.svg").str(), "uri.svg"); + ASSERT_EQ(URI("tmp/uri.svg").str(), "tmp/uri.svg"); + ASSERT_EQ(URI("/tmp/uri.svg").str(), "/tmp/uri.svg"); + ASSERT_EQ(URI("../uri.svg").str(), "../uri.svg"); + + ASSERT_EQ(URI("file:///tmp/uri.svg").str(), "file:///tmp/uri.svg"); + ASSERT_EQ(URI("uri.svg", "file:///tmp/").str(), "file:///tmp/uri.svg"); + ASSERT_EQ(URI("file:///tmp/uri.svg").str("file:///tmp/"), "uri.svg"); + ASSERT_EQ(URI("file:///tmp/up/uri.svg").str("file:///tmp/"), "up/uri.svg"); + ASSERT_EQ(URI("file:///tmp/uri.svg").str("file:///tmp/up/"), "../uri.svg"); + ASSERT_EQ(URI("file:///tmp/uri.svg").str("http://web/url"), "file:///tmp/uri.svg"); + ASSERT_EQ(URI("file:///tmp/uri.svg").str("http://web/url"), "file:///tmp/uri.svg"); + ASSERT_EQ(URI("foo/uri.svg", "http://web/a/b/c").str(), "http://web/a/b/foo/uri.svg"); + ASSERT_EQ(URI("foo/uri.svg", "http://web/a/b/c").str("http://web/a/"), "b/foo/uri.svg"); + ASSERT_EQ(URI("foo/uri.svg", "http://web/a/b/c").str("http://other/a/"), "http://web/a/b/foo/uri.svg"); + + ASSERT_EQ(URI("http://web/").str("http://web/"), ""); + ASSERT_EQ(URI("http://web/").str("http://web/url"), "./"); + + // special case: don't cross filesystem root + ASSERT_EQ(URI("file:///a").str("file:///"), "a"); + ASSERT_EQ(URI("file:///ax/b").str("file:///ay/"), "file:///ax/b"); // special case + ASSERT_EQ(URI("file:///C:/b").str("file:///D:/"), "file:///C:/b"); // special case + ASSERT_EQ(URI("file:///C:/a/b").str("file:///C:/b/"), "../a/b"); + + ASSERT_EQ(URI(win_url_unc).str(), win_url_unc); + ASSERT_EQ(URI(win_url_unc).str("file://laptop/My%20Documents/"), "FileSchemeURIs.doc"); + ASSERT_EQ(URI(win_url_local).str(), win_url_local); + ASSERT_EQ(URI(win_url_local).str("file:///C:/Documents%20and%20Settings/"), "davris/FileSchemeURIs.doc"); + ASSERT_EQ(URI(win_url_local).str(win_url_unc), win_url_local); +#ifdef _WIN32 + ASSERT_EQ(URI(win_url_local).toNativeFilename(), win_filename_local); +#else + ASSERT_EQ(URI("file:///tmp/uri.svg").toNativeFilename(), "/tmp/uri.svg"); + ASSERT_EQ(URI("file:///tmp/x%20y.svg").toNativeFilename(), "/tmp/x y.svg"); + ASSERT_EQ(URI("file:///a/b#hash").toNativeFilename(), "/a/b"); +#endif + + ASSERT_ANY_THROW(URI("http://a/b").toNativeFilename()); +} + +TEST(UriTest, StrDataScheme) +{ + ASSERT_EQ(URI("data:,text").str(), "data:,text"); + ASSERT_EQ(URI("data:,white%20space").str(), "data:,white%20space"); + ASSERT_EQ(URI("data:,umlaut-%C3%96").str(), "data:,umlaut-%C3%96"); + ASSERT_EQ(URI(DATA_BASE64_HELLO_WORLD).str(), DATA_BASE64_HELLO_WORLD); +} + +TEST(UriTest, Escape) +{ + ASSERT_EQ(URI("data:,white space").str(), "data:,white%20space"); + ASSERT_EQ(URI("data:,white\nspace").str(), "data:,white%0Aspace"); + ASSERT_EQ(URI("data:,umlaut-\xC3\x96").str(), "data:,umlaut-%C3%96"); +} + +TEST(UriTest, GetContents) +{ + ASSERT_EQ(URI("data:,white space").getContents(), "white space"); + ASSERT_EQ(URI("data:,white%20space").getContents(), "white space"); + ASSERT_EQ(URI("data:,white\nspace").getContents(), "white\nspace"); + ASSERT_EQ(URI("data:,white%0Aspace").getContents(), "white\nspace"); + ASSERT_EQ(URI("data:,umlaut-%C3%96").getContents(), "umlaut-\xC3\x96"); + ASSERT_EQ(URI(DATA_BASE64_HELLO_WORLD).getContents(), "Hello World"); + ASSERT_EQ(URI(DATA_BASE64_HELLO_WORLD_WRAPPED).getContents(), "Hello World"); + + ASSERT_ANY_THROW(URI().getContents()); +} + +TEST(UriTest, CssStr) +{ + ASSERT_EQ(URI("file:///tmp/uri.svg").cssStr(), "url(file:///tmp/uri.svg)"); + ASSERT_EQ(URI("uri.svg").cssStr(), "url(uri.svg)"); +} + +TEST(UriTest, GetMimeType) +{ + ASSERT_EQ(URI("data:image/png;base64,").getMimeType(), "image/png"); + ASSERT_EQ(URI("data:text/plain,xxx").getMimeType(), "text/plain"); + ASSERT_EQ(URI("file:///tmp/uri.png").getMimeType(), "image/png"); + ASSERT_EQ(URI("uri.png").getMimeType(), "image/png"); + ASSERT_EQ(URI("uri.svg").getMimeType(), "image/svg+xml"); + + // can be "text/plain" or "text/*" + ASSERT_EQ(URI("file:///tmp/uri.txt").getMimeType().substr(0, 5), "text/"); +} + +TEST(UriTest, HasScheme) +{ + ASSERT_FALSE(URI().hasScheme("file")); + ASSERT_FALSE(URI("uri.svg").hasScheme("file")); + ASSERT_FALSE(URI("uri.svg").hasScheme("data")); + + ASSERT_TRUE(URI("file:///uri.svg").hasScheme("file")); + ASSERT_TRUE(URI("FILE:///uri.svg").hasScheme("file")); + ASSERT_FALSE(URI("file:///uri.svg").hasScheme("data")); + + ASSERT_TRUE(URI("data:,").hasScheme("data")); + ASSERT_TRUE(URI("DaTa:,").hasScheme("data")); + ASSERT_FALSE(URI("data:,").hasScheme("file")); + + ASSERT_TRUE(URI("http://web/").hasScheme("http")); + ASSERT_FALSE(URI("http://web/").hasScheme("file")); + + ASSERT_TRUE(URI::from_href_and_basedir("data:,white\nspace", "/tmp").hasScheme("data")); +} + +TEST(UriTest, isOpaque) +{ + ASSERT_FALSE(URI().isOpaque()); + ASSERT_FALSE(URI("file:///uri.svg").isOpaque()); + ASSERT_FALSE(URI("/uri.svg").isOpaque()); + ASSERT_FALSE(URI("uri.svg").isOpaque()); + ASSERT_FALSE(URI("foo://bar/baz").isOpaque()); + ASSERT_FALSE(URI("foo://bar").isOpaque()); + ASSERT_FALSE(URI("foo:/bar").isOpaque()); + + ASSERT_TRUE(URI("foo:bar").isOpaque()); + ASSERT_TRUE(URI("mailto:user@host.xy").isOpaque()); + ASSERT_TRUE(URI("news:comp.lang.java").isOpaque()); +} + +TEST(UriTest, isRelative) +{ + ASSERT_TRUE(URI().isRelative()); + + ASSERT_FALSE(URI("http://web/uri.svg").isRelative()); + ASSERT_FALSE(URI("file:///uri.svg").isRelative()); + ASSERT_FALSE(URI("mailto:user@host.xy").isRelative()); + ASSERT_FALSE(URI("data:,").isRelative()); + + ASSERT_TRUE(URI("//web/uri.svg").isRelative()); + ASSERT_TRUE(URI("/uri.svg").isRelative()); + ASSERT_TRUE(URI("uri.svg").isRelative()); + ASSERT_TRUE(URI("./uri.svg").isRelative()); + ASSERT_TRUE(URI("../uri.svg").isRelative()); +} + +TEST(UriTest, isNetPath) +{ + ASSERT_FALSE(URI().isNetPath()); + ASSERT_FALSE(URI("http://web/uri.svg").isNetPath()); + ASSERT_FALSE(URI("file:///uri.svg").isNetPath()); + ASSERT_FALSE(URI("/uri.svg").isNetPath()); + ASSERT_FALSE(URI("uri.svg").isNetPath()); + + ASSERT_TRUE(URI("//web/uri.svg").isNetPath()); +} + +TEST(UriTest, isRelativePath) +{ + ASSERT_FALSE(URI("foo:bar").isRelativePath()); + ASSERT_TRUE(URI("foo%3Abar").isRelativePath()); + + ASSERT_FALSE(URI("http://web/uri.svg").isRelativePath()); + ASSERT_FALSE(URI("//web/uri.svg").isRelativePath()); + ASSERT_FALSE(URI("/uri.svg").isRelativePath()); + + ASSERT_TRUE(URI("uri.svg").isRelativePath()); + ASSERT_TRUE(URI("./uri.svg").isRelativePath()); + ASSERT_TRUE(URI("../uri.svg").isRelativePath()); +} + +TEST(UriTest, isAbsolutePath) +{ + ASSERT_FALSE(URI().isAbsolutePath()); + ASSERT_FALSE(URI("http://web/uri.svg").isAbsolutePath()); + ASSERT_FALSE(URI("//web/uri.svg").isAbsolutePath()); + ASSERT_FALSE(URI("uri.svg").isAbsolutePath()); + ASSERT_FALSE(URI("../uri.svg").isAbsolutePath()); + + ASSERT_TRUE(URI("/uri.svg").isAbsolutePath()); +} + +TEST(UriTest, getScheme) +{ + ASSERT_STREQ(URI().getScheme(), nullptr); + + ASSERT_STREQ(URI("https://web/uri.svg").getScheme(), "https"); + ASSERT_STREQ(URI("file:///uri.svg").getScheme(), "file"); + ASSERT_STREQ(URI("data:,").getScheme(), "data"); + + ASSERT_STREQ(URI("data").getScheme(), nullptr); +} + +TEST(UriTest, getQuery) +{ + ASSERT_STREQ(URI().getQuery(), nullptr); + ASSERT_STREQ(URI("uri.svg?a=b&c=d").getQuery(), "a=b&c=d"); + ASSERT_STREQ(URI("?a=b&c=d#hash").getQuery(), "a=b&c=d"); +} + +TEST(UriTest, getFragment) +{ + ASSERT_STREQ(URI().getFragment(), nullptr); + ASSERT_STREQ(URI("uri.svg").getFragment(), nullptr); + ASSERT_STREQ(URI("uri.svg#hash").getFragment(), "hash"); + ASSERT_STREQ(URI("?a=b&c=d#hash").getFragment(), "hash"); + ASSERT_STREQ(URI("urn:isbn:096139210x#hash").getFragment(), "hash"); +} + +TEST(UriTest, getOpaque) +{ + ASSERT_STREQ(URI().getOpaque(), nullptr); + ASSERT_STREQ(URI("urn:isbn:096139210x#hash").getOpaque(), "isbn:096139210x"); + ASSERT_STREQ(URI("data:,foo").getOpaque(), ",foo"); +} + +TEST(UriTest, from_native_filename) +{ +#ifdef _WIN32 + ASSERT_EQ(URI::from_native_filename(win_filename_local).str(), win_url_local); +#else + ASSERT_EQ(URI::from_native_filename("/tmp/uri.svg").str(), "file:///tmp/uri.svg"); + ASSERT_EQ(URI::from_native_filename("/tmp/x y.svg").str(), "file:///tmp/x%20y.svg"); +#endif +} + +TEST(UriTest, uri_to_iri) +{ + // unescape UTF-8 (U+00D6) + ASSERT_EQ(Inkscape::uri_to_iri("data:,umlaut-%C3%96"), "data:,umlaut-\xC3\x96"); + // don't unescape ASCII (U+003A) + ASSERT_EQ(Inkscape::uri_to_iri("foo%3Abar"), "foo%3Abar"); + // sequence (U+00D6 U+1F37A U+003A) + ASSERT_EQ(Inkscape::uri_to_iri("%C3%96%F0%9F%8D%BA%3A"), "\xC3\x96\xF0\x9F\x8D\xBA%3A"); +} + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/testfiles/src/util-test.cpp b/testfiles/src/util-test.cpp new file mode 100644 index 0000000..35a3cf3 --- /dev/null +++ b/testfiles/src/util-test.cpp @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** + * @file + * Test utilities from src/util + */ +/* + * Authors: + * Thomas Holder + * Martin Owens + * + * Copyright (C) 2020-2022 Authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include "gtest/gtest.h" +#include "util/longest-common-suffix.h" +#include "util/parse-int-range.h" + +TEST(UtilTest, NearestCommonAncestor) +{ +#define nearest_common_ancestor(a, b, c) \ + Inkscape::Algorithms::nearest_common_ancestor(a, b, c) + + // simple node with a parent + struct Node + { + Node const *parent; + Node(Node const *p) : parent(p){}; + Node(Node const &other) = delete; + }; + + // iterator which traverses towards the root node + struct iter + { + Node const *node; + iter(Node const &n) : node(&n) {} + bool operator==(iter const &rhs) const { return node == rhs.node; } + bool operator!=(iter const &rhs) const { return node != rhs.node; } + iter &operator++() + { + node = node->parent; + return *this; + } + + // TODO remove, the implementation should not require this + Node const &operator*() const { return *node; } + }; + + // construct a tree + auto const node0 = Node(nullptr); + auto const node1 = Node(&node0); + auto const node2 = Node(&node1); + auto const node3a = Node(&node2); + auto const node4a = Node(&node3a); + auto const node5a = Node(&node4a); + auto const node3b = Node(&node2); + auto const node4b = Node(&node3b); + auto const node5b = Node(&node4b); + + // start at each node from 5a to 0 (first argument) + ASSERT_EQ(nearest_common_ancestor(iter(node5a), iter(node5b), iter(node0)), iter(node2)); + ASSERT_EQ(nearest_common_ancestor(iter(node4a), iter(node5b), iter(node0)), iter(node2)); + ASSERT_EQ(nearest_common_ancestor(iter(node3a), iter(node5b), iter(node0)), iter(node2)); + ASSERT_EQ(nearest_common_ancestor(iter(node2), iter(node5b), iter(node0)), iter(node2)); + ASSERT_EQ(nearest_common_ancestor(iter(node1), iter(node5b), iter(node0)), iter(node1)); + ASSERT_EQ(nearest_common_ancestor(iter(node0), iter(node5b), iter(node0)), iter(node0)); + + // start at each node from 5b to 0 (second argument) + ASSERT_EQ(nearest_common_ancestor(iter(node5a), iter(node5b), iter(node0)), iter(node2)); + ASSERT_EQ(nearest_common_ancestor(iter(node5a), iter(node4b), iter(node0)), iter(node2)); + ASSERT_EQ(nearest_common_ancestor(iter(node5a), iter(node3b), iter(node0)), iter(node2)); + ASSERT_EQ(nearest_common_ancestor(iter(node5a), iter(node2), iter(node0)), iter(node2)); + ASSERT_EQ(nearest_common_ancestor(iter(node5a), iter(node1), iter(node0)), iter(node1)); + ASSERT_EQ(nearest_common_ancestor(iter(node5a), iter(node0), iter(node0)), iter(node0)); + + // identity (special case in implementation) + ASSERT_EQ(nearest_common_ancestor(iter(node5a), iter(node5a), iter(node0)), iter(node5a)); + + // identical parents (special case in implementation) + ASSERT_EQ(nearest_common_ancestor(iter(node3a), iter(node3b), iter(node0)), iter(node2)); +} + +TEST(UtilTest, ParseIntRangeTest) +{ + // Single number + ASSERT_EQ(Inkscape::parseIntRange("1"), std::set({1})); + ASSERT_EQ(Inkscape::parseIntRange("3"), std::set({3})); + + // Out of range numbers + ASSERT_EQ(Inkscape::parseIntRange("11", 1, 10), std::set({})); + ASSERT_EQ(Inkscape::parseIntRange("3", 5, 10), std::set({})); + ASSERT_EQ(Inkscape::parseIntRange("3", 5), std::set({})); + + // Comma seperated in various orders + ASSERT_EQ(Inkscape::parseIntRange("1,3,5"), std::set({1, 3, 5})); + ASSERT_EQ(Inkscape::parseIntRange("3,1,4"), std::set({1, 3, 4})); + ASSERT_EQ(Inkscape::parseIntRange("3 ,2,9,"), std::set({2, 3, 9})); + + // Range of numbers using a dash + ASSERT_EQ(Inkscape::parseIntRange("1-4"), std::set({1, 2, 3, 4})); + ASSERT_EQ(Inkscape::parseIntRange("2-4"), std::set({2, 3, 4})); + ASSERT_EQ(Inkscape::parseIntRange("-"), std::set({1})); // 1 is the implied start + ASSERT_EQ(Inkscape::parseIntRange("-3"), std::set({1, 2, 3})); + ASSERT_EQ(Inkscape::parseIntRange("8-"), std::set({8})); + ASSERT_EQ(Inkscape::parseIntRange("-", 4, 6), std::set({4, 5, 6})); + ASSERT_EQ(Inkscape::parseIntRange("-7", 5), std::set({5, 6, 7})); + ASSERT_EQ(Inkscape::parseIntRange("8-", 1, 10), std::set({8, 9, 10})); + ASSERT_EQ(Inkscape::parseIntRange("all", 4, 6), std::set({4, 5, 6})); + + // Mixeed formats + ASSERT_EQ(Inkscape::parseIntRange("2-4,7-9", 1, 10), std::set({2,3,4,7,8,9})); +} + +// vim: filetype=cpp:expandtab:shiftwidth=4:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/testfiles/src/visual-bounds-test.cpp b/testfiles/src/visual-bounds-test.cpp new file mode 100644 index 0000000..dbc1605 --- /dev/null +++ b/testfiles/src/visual-bounds-test.cpp @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** + * @file Test the computation of visual bounding boxes. + */ +/* + * Authors: + * RafaƂ Siejakowski + * + * Copyright (C) 2023 Authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ +#include + +#include <2geom/rect.h> +#include +#include +#include "inkscape.h" +#include "document.h" +#include "object/sp-item.h" +#include "object/sp-rect.h" + +class InkscapeInit // Initialize the Inkscape Application singleton. +{ +public: + InkscapeInit() + { + if (!Inkscape::Application::exists()) { + Inkscape::Application::create(false); + } + } +}; + +class VisualBoundsTest : public ::testing::Test +{ +protected: + + VisualBoundsTest() + : _init{} + , _document{SPDocument::createNewDoc(INKSCAPE_TESTS_DIR "/data/visual-bounds.svg", false)} + { + _document->ensureUpToDate(); + _findTestCount(); + } + +public: + SPItem *getItemById(char const *const id) + { + auto obj = _document->getObjectById(id); + if (!obj) { + return nullptr; + } + return cast(obj); + } + size_t testCount() const { return _test_count; } +private: + void _findTestCount() + { + auto tspan = _document->getObjectById("num_tests"); + if (!tspan) { + std::cerr << "Could not get the element with id=\"num_tests\".\n"; + return; + } + auto content = tspan->firstChild(); + if (!content) { + std::cerr << "Could not get the content of the element with id=\"num_tests\".\n"; + return; + } + auto repr = content->getRepr(); + if (!repr) { + std::cerr << "Could not get the repr of the content of the element with id=\"num_tests\".\n"; + return; + } + auto text = repr->content(); + if (!text) { + std::cerr << "Could not get the text content of the element with id=\"num_tests\".\n"; + return; + } + try { + _test_count = std::stoul(text); + } catch (std::invalid_argument const &e) { + std::cerr << "Could not parse an integer from the content of element with id=\"num_tests\".\n"; + return; + } + } + + InkscapeInit _init; + std::unique_ptr _document; + size_t _test_count = 0; +}; + +TEST_F(VisualBoundsTest, ShapeBounds) +{ + size_t const id_maxlen = 7 + 1; + char object_id[id_maxlen], bbox_id[id_maxlen]; + double const epsilon = 1e-4; + + for (size_t i = 1; i<= testCount(); i++) { + snprintf(object_id, id_maxlen, "obj-%lu", i); + snprintf(bbox_id, id_maxlen, "vbb-%lu", i); + + auto const *item = getItemById(object_id); + auto const *bbox = getItemById(bbox_id); + ASSERT_TRUE(item && bbox); + + Geom::Rect const expected_bbox = cast(bbox)->getRect(); + + auto const actual_opt_bbox = item->visualBounds(item->transform); + ASSERT_TRUE(bool(actual_opt_bbox)); + Geom::Rect const actual_bbox = *actual_opt_bbox; + + // Check that the item's visual bounding box is as expected, up to epsilon. + for (auto const dim : {Geom::X, Geom::Y}) { + EXPECT_GE(actual_bbox[dim].min(), expected_bbox[dim].min() - epsilon); + EXPECT_LE(actual_bbox[dim].min(), expected_bbox[dim].min() + epsilon); + + EXPECT_GE(actual_bbox[dim].max(), expected_bbox[dim].max() - epsilon); + EXPECT_LE(actual_bbox[dim].max(), expected_bbox[dim].max() + epsilon); + } + } +} + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : \ No newline at end of file diff --git a/testfiles/src/xml-test.cpp b/testfiles/src/xml-test.cpp new file mode 100644 index 0000000..dee272d --- /dev/null +++ b/testfiles/src/xml-test.cpp @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/** + * @file + * Test xml node + */ +/* + * Authors: + * Ted Gould + * + * Copyright (C) 2020 Authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include "gtest/gtest.h" +#include "xml/repr.h" + +TEST(XmlTest, nodeiter) +{ + auto testdoc = std::shared_ptr(sp_repr_read_buf("", SP_SVG_NS_URI)); + ASSERT_TRUE(testdoc); + + auto count = 0; + for (auto &child : *testdoc->root()) { + ASSERT_STREQ(child.name(), "svg:g"); + count++; + } + ASSERT_EQ(count, 1); + + testdoc = + std::shared_ptr(sp_repr_read_buf("", SP_SVG_NS_URI)); + ASSERT_TRUE(testdoc); + + count = 0; + for (auto &child : *testdoc->root()) { + ASSERT_STREQ(child.name(), "svg:g"); + count++; + } + ASSERT_EQ(count, 3); + + testdoc = std::shared_ptr(sp_repr_read_buf(R"""( + + + + + + + + + + + + +)""", SP_SVG_NS_URI)); + ASSERT_TRUE(testdoc); + + auto path = std::list{"svg:g", "svg:path"}; + auto found = testdoc->root()->findChildPath(path); + ASSERT_NE(found, nullptr); + ASSERT_STREQ(found->attribute("id"), "b"); + + // no such second element + path = {"svg:g", "svg:g"}; + ASSERT_EQ(testdoc->root()->findChildPath(path), nullptr); + + // no such first element + path = {"svg:symbol", "svg:path"}; + ASSERT_EQ(testdoc->root()->findChildPath(path), nullptr); + + // root with no children + testdoc = std::shared_ptr(sp_repr_read_buf("", SP_SVG_NS_URI)); + ASSERT_EQ(testdoc->root()->findChildPath(path), nullptr); +} + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : diff --git a/testfiles/unittest.cpp b/testfiles/unittest.cpp new file mode 100644 index 0000000..0135781 --- /dev/null +++ b/testfiles/unittest.cpp @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Unit test main. + * + * Author: + * Jon A. Cruz + * + * Copyright (C) 2015 Authors + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#include + +#include +#include + +#include "inkgc/gc-core.h" +#include "util/statics.h" + +int main(int argc, char **argv) +{ + // setup general environment +#if !GLIB_CHECK_VERSION(2,36,0) + g_type_init(); +#endif + + // If possible, unit tests shouldn't require a GUI session + // since this won't generally be available in auto-builders + + Gio::init(); + + Inkscape::GC::init(); + + ::testing::InitGoogleTest(&argc, argv); + auto ret = RUN_ALL_TESTS(); + + Inkscape::Util::StaticsBin::get().destroy(); + return ret; +} + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: expandtab:shiftwidth=4:tabstop=8:softtabstop=4 : diff --git a/testfiles/utils/functions.sh b/testfiles/utils/functions.sh new file mode 100644 index 0000000..4524cbe --- /dev/null +++ b/testfiles/utils/functions.sh @@ -0,0 +1,77 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Bash functions useful for fuzzy bitmap comparisons. This file is a part of Inkscape. +# +# Authors: +# Rafael Siejakowski +# +# Copyright (C) 2023 Authors +# +# Released under GNU GPL v2+, read the file 'COPYING' for more information. +# + +ensure_command() +{ + command -v $1 >/dev/null 2>&1 || { echo >&2 "Required command '$1' not found. Aborting."; exit 1; } +} + +export LANG=C # Needed to force . as the decimal separator +ensure_command "bc" + +# Parse out the relative difference between two images from the command line output +# of an ImageMagick compare command. In case of error, crash out of the script to ensure the test fails. +# +# Arguments: +# $1 - commandline output from a compare command with RMSE metric. +# +# Output: +# The parsed relative error, as a floating point number. +# +get_compare_result() +{ + local COMPARE_OUTPUT="$1" + RELATIVE_ERROR=${COMPARE_OUTPUT#*(} + RELATIVE_ERROR=${RELATIVE_ERROR%)*} + if [[ "x$RELATIVE_ERROR" == "x" ]] + then + echo "Warning: Could not parse out the relative error from ImageMagick output." >&2 + exit 42 + fi + echo "$RELATIVE_ERROR" +} + +# Check whether a floating point number is less than or equal to a percentage value. +# In case of error, crash out of the script. +# +# Arguments: +# $1 - a floating pointing number between 0.0 and 1.0 +# $2 - a number between 0.0 and 100.0 representing a percentage. Scientific notation not allowed. +# +# Output: +# 1 if and only if $1 * 100 <= $2 else 0. +# +is_relative_error_within_tolerance() +{ + local CONDITION=$(printf "%.12f * 100 <= $2" "$1") + WITHIN_TOLERANCE=$(echo "${CONDITION}" | bc) + if [[ $? -ne 0 || ( $WITHIN_TOLERANCE -ne 0 && $WITHIN_TOLERANCE -ne 1 ) ]] + then + echo "Warning: An error occurred running 'bc'." >&2 + exit 42 + fi + echo "$WITHIN_TOLERANCE" +} + +# Multiply a floating point number by 100. +# +# Arguments: +# $1 - a floating point number. +# +# Output: +# The result of multiplying the passed number by 100, rounded to 1 digit after the decimal point. +fraction_to_percentage() +{ + local FORMULA=$(printf "%.4f * 100" "$1") + echo "$FORMULA" | bc +} + -- cgit v1.2.3